前言
windows常见的文件操作有日志、操作配置文件、ini、注册表、音视频的文件存储。
而linux系统具有一切皆文件的概念。
正文
c/c++那会都是打开一个文件然后以什么模式,用完还要关闭。
c/c++ win32 mfc文件操作
c文件操作
fopen
函数
1 2 3 4
| _ACRTIMP FILE* __cdecl fopen( _In_z_ char const* _FileName, _In_z_ char const* _Mode );
|
fopen_s
函数
1 2 3 4 5
| _ACRTIMP errno_t __cdecl fopen_s( _Outptr_result_nullonfailure_ FILE** _Stream, _In_z_ char const* _FileName, _In_z_ char const* _Mode );
|
fwrite
函数
1 2 3 4 5 6
| _ACRTIMP size_t __cdecl fwrite( _In_reads_bytes_(_ElementSize * _ElementCount) void const* _Buffer, _In_ size_t _ElementSize, _In_ size_t _ElementCount, _Inout_ FILE* _Stream );
|
带s一般都是所谓的安全函数
几种模式_Mode
- a add
- r read
- w weite
-
然后随便写个按钮绑定一下代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| void CMyCFileDlg::OnBnClickedWriteFile(){ FILE *pFile = fopen("1.txt", "w"); if (pFile == NULL){ MessageBox(_T("文件打开失败")); return; }
char szBuf[1024] = "c language file";
fwrite(szBuf, 1, strlen(szBuf)+1, pFile);
fclose(pFile); }
|
点击按钮之后文件夹目录就会多了这个1.txt,内容也是我们写的szBuf。
具体可以看文件创建时间是否符合我们刚才按下按钮的时候。
然后使用读文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| void CMyCFileDlg::OnBnClickedReadFile(){ FILE *pFile = fopen("1.txt", "r"); if (pFile == NULL){ MessageBox(_T("文件打开失败")); return; }
char szbuf[1024] = { 0 }; int len = fread(szbuf, 1, 1024, pFile); fclose(pFile); MessageBox(szbuf); }
|
实现起来也比较简单。
不过这里有个点要注意,因为读文件的时候文件不一定就写满了1024.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| void CMyCFileDlg::OnBnClickedReadFile(){ FILE *pFile = fopen("1.txt", "r"); if (pFile == NULL){ MessageBox(_T("文件打开失败")); return; }
char szbuf[1024] = { 0 }; fseek(pFile, 0, SEEK_END); int fLen = ftell(pFile); fseek(pFile, 0, SEEK_SET); int len = fread(szbuf, 1, fLen, pFile); fclose(pFile); MessageBox(szbuf); }
|
要注意偏移之后要让指针回到起始位置。
c++文件操作
c++是以类作为核心的语言,所以文件也通过一个类读写。std::ofstream
使用前记得
#include
using namespace std;
不然没法用这个类。解锁命名空间也是为了写着方便
1 2 3 4 5 6 7
| void CMyCFileDlg::OnBnClickedWriteFile(){ ofstream ofs("2.txt"); char szBuf[1024] = "c++ file edit"; ofs.write(szBuf, strlen(szBuf) + 1); ofs.close(); }
|
写起来也非常简单
顺便把读文件也写了,到时候再看。
1 2 3 4 5 6 7 8
| void CMyCFileDlg::OnBnClickedReadFile(){ ifstream ifs("2.txt"); char szBuf[1024] = { 0 }; ifs.read(szBuf, 1024); ifs.close(); MessageBox(szBuf); }
|
效果自然是没啥问题的。
win32 api
其实跟之前进程用到过
1 2 3 4 5 6 7 8 9
| CreateFileW( _In_ LPCWSTR lpFileName, _In_ DWORD dwDesiredAccess, _In_ DWORD dwShareMode, _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes, _In_ DWORD dwCreationDisposition, _In_ DWORD dwFlagsAndAttributes, _In_opt_ HANDLE hTemplateFile );
|
至于a和w就是多字节和Unicode的区别。
写文件的方式
1 2 3 4 5 6 7
| WriteFile( _In_ HANDLE hFile, _In_reads_bytes_opt_(nNumberOfBytesToWrite) LPCVOID lpBuffer, _In_ DWORD nNumberOfBytesToWrite, _Out_opt_ LPDWORD lpNumberOfBytesWritten, _Inout_opt_ LPOVERLAPPED lpOverlapped );
|
随便写写。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| void CMyCFileDlg::OnBnClickedWriteFile(){ HANDLE hFile; hFile = CreateFile("3.txt", GENERIC_WRITE, NULL, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL ); if (hFile == INVALID_HANDLE_VALUE){ MessageBox("创建文件对象失败"); return; }
DWORD dwWrite; char szBuf[1024] = "win32 api edit file"; WriteFile(hFile, szBuf, strlen(szBuf) + 1, &dwWrite, NULL);
CloseHandle(hFile); }
void CMyCFileDlg::OnBnClickedReadFile(){ HANDLE hFile; hFile = CreateFile("3.txt", GENERIC_READ, NULL, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); if (hFile == INVALID_HANDLE_VALUE){ MessageBox("创建文件对象失败"); return; }
DWORD dwRead; char szBuf[1024] = { 0 }; ReadFile(hFile, szBuf, 1024, &dwRead, NULL);
CloseHandle(hFile); MessageBox(szBuf); }
|
这里有点比较麻烦,就是write的时候,好像只能创建一次
hFile = CreateFile("3.txt", GENERIC_WRITE, NULL, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
其中CREATE_NEW
就是新建的意思,但是如果存在了他就会报错,但改成其它的感觉又不太合适。先凑合用。
MFC
写法跟c++比较类似吧
1 2 3 4 5 6 7
| void CMyCFileDlg::OnBnClickedWriteFile(){ CFile cf("4.txt", CFile::modeCreate | CFile::modeWrite); char szBuf[1024] = "mfc edit files"; cf.Write(szBuf, strlen(szBuf) + 1); cf.Close(); }
|
1 2 3 4 5 6 7 8
| void CMyCFileDlg::OnBnClickedReadFile(){ CFile cf("4.txt", CFile::modeRead); char szBuf[1024] = { 0 }; cf.Read(szBuf, 1024); cf.Close(); MessageBox(szBuf); }
|
这样最基础的操作肯定是没问题的。
不过读文件好像还有别的骚操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| void CMyCFileDlg::OnBnClickedReadFile(){
CFileDialog fileDlg(TRUE); fileDlg.m_ofn.lpstrTitle = "Test"; fileDlg.m_ofn.lpstrFilter = "Text Files(*.txt)\0*.txt\0All Files(*.*)\0*.*\0\0"; if (IDOK == fileDlg.DoModal()){ CFile cf(fileDlg.GetFileName(), CFile::modeRead); DWORD dwFileLen = cf.GetLength(); char szBuf[1024] = { 0 }; cf.Read(szBuf, dwFileLen); cf.Close(); MessageBox(szBuf); } }
|
点击读文件他会弹出一个文件夹让你选。
然后根据我们的过滤器,一种是text一种是all
随便打开其中一个都能读出来
配置文件的操作
配置文件的格式比较特殊,.ini
里面一般都是配置选项。
WritePrivateProfileString
1 2 3 4 5 6
| WritePrivateProfileStringW( _In_opt_ LPCWSTR lpAppName, _In_opt_ LPCWSTR lpKeyName, _In_opt_ LPCWSTR lpString, _In_opt_ LPCWSTR lpFileName );
|
w和a就是对这些字符的要求不一样。
然后瞎写一个
1 2 3 4 5 6 7 8 9 10 11
| void CMyCFileDlg::OnBnClickedWriteFile(){ char szPath[MAX_PATH] = { 0 }; GetCurrentDirectory(MAX_PATH, szPath); CString szPathFile; szPathFile.Format("%s\\Test.ini", szPath); WritePrivateProfileString("man", "friend", "张三", szPathFile); WritePrivateProfileString("man", "student", "李四", szPathFile); WritePrivateProfileString("school", "teacher", "王五", szPathFile); }
|
启动之后点击写文件
可以在文件夹目录下看到我们写的配置。
至于读文件,也有点相似吧
要用到这个玩意
1 2 3 4 5 6 7 8
| GetPrivateProfileStringW( _In_opt_ LPCWSTR lpAppName, _In_opt_ LPCWSTR lpKeyName, _In_opt_ LPCWSTR lpDefault, _Out_writes_to_opt_(nSize, return + 1) LPWSTR lpReturnedString, _In_ DWORD nSize, _In_opt_ LPCWSTR lpFileName );
|
额,这里ini格式瞎写的,所以读出来的时候可能看着太怪了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| void CMyCFileDlg::OnBnClickedReadFile(){
char szPath[MAX_PATH] = { 0 }; GetCurrentDirectory(MAX_PATH, szPath); CString szPathFile; szPathFile.Format("%s\\Test.ini", szPath);
char dwKey[1024] = { 0 }; char dwKeyName[1024] = { 0 }; char dwValue[1024] = { 0 }; GetPrivateProfileString("man", "friend", NULL, dwKey, 1024, szPathFile); GetPrivateProfileString("man", "student", NULL, dwKeyName, 1024, szPathFile); GetPrivateProfileString("school", "teacher", NULL, dwValue, 1024, szPathFile);
CString strShow; strShow.Format("friend:%s student:%s teacher:%s", dwKey, dwKeyName, dwValue); MessageBox(strShow); }
|
但反正最后还是读出了值:
注册表
注册表是存储在二进制文件里面的,win32api 提供了大量的函数操作注册表
额,默认打开的话,win+r是打开运行,然后输入regedit即可打开注册表,如果之前有用过就会很熟悉。
动注册表之前,vs需要用管理员启动,不然肯定是无法写入的。
项目还是之前那个没关系。
RegCreateKey
创建指定的注册表项
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| WINADVAPI LSTATUS APIENTRY RegCreateKeyA ( _In_ HKEY hKey, _In_opt_ LPCSTR lpSubKey, _Out_ PHKEY phkResult ); WINADVAPI LSTATUS APIENTRY RegCreateKeyW ( _In_ HKEY hKey, _In_opt_ LPCWSTR lpSubKey, _Out_ PHKEY phkResult ); #ifdef UNICODE #define RegCreateKey RegCreateKeyW #else #define RegCreateKey RegCreateKeyA #endif
|
这个微软就喜欢多字节和Unicode,从他头文件中各自宏定义去兼容这两种编码。
RegOpenKey
打开注册表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| WINADVAPI LSTATUS APIENTRY RegOpenKeyA ( _In_ HKEY hKey, _In_opt_ LPCSTR lpSubKey, _Out_ PHKEY phkResult ); WINADVAPI LSTATUS APIENTRY RegOpenKeyW ( _In_ HKEY hKey, _In_opt_ LPCWSTR lpSubKey, _Out_ PHKEY phkResult ); #ifdef UNICODE #define RegOpenKey RegOpenKeyW #else #define RegOpenKey RegOpenKeyA #endif
|
RegSetValue
写入注册表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| WINADVAPI LSTATUS APIENTRY RegSetValueA ( _In_ HKEY hKey, _In_opt_ LPCSTR lpSubKey, _In_ DWORD dwType, _In_reads_bytes_opt_(cbData) LPCSTR lpData, _In_ DWORD cbData ); WINADVAPI LSTATUS APIENTRY RegSetValueW ( _In_ HKEY hKey, _In_opt_ LPCWSTR lpSubKey, _In_ DWORD dwType, _In_reads_bytes_opt_(cbData) LPCWSTR lpData, _In_ DWORD cbData ); #ifdef UNICODE #define RegSetValue RegSetValueW #else #define RegSetValue RegSetValueA #endif
|
RegQueryValue
检索与指定注册表项的默认值或未命名值关联的数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| WINADVAPI LSTATUS APIENTRY RegQueryValueA ( _In_ HKEY hKey, _In_opt_ LPCSTR lpSubKey, _Out_writes_bytes_to_opt_(*lpcbData, *lpcbData) __out_data_source(REGISTRY) LPSTR lpData, _Inout_opt_ PLONG lpcbData ); WINADVAPI LSTATUS APIENTRY RegQueryValueW ( _In_ HKEY hKey, _In_opt_ LPCWSTR lpSubKey, _Out_writes_bytes_to_opt_(*lpcbData, *lpcbData) __out_data_source(REGISTRY) LPWSTR lpData, _Inout_opt_ PLONG lpcbData ); #ifdef UNICODE #define RegQueryValue RegQueryValueW #else #define RegQueryValue RegQueryValueA #endif
|
那么写入的部分其实还是有点水
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| void CMyCFileDlg::OnBnClickedWriteFile(){ HKEY hKey; DWORD dwWeight = 70;
DWORD dwRet = ::RegCreateKey(HKEY_CURRENT_USER, "Software\\HHH\\admin", &hKey); if (dwRet != ERROR_SUCCESS){ MessageBox("创建注册表失败"); return; } dwRet = ::RegSetValueEx(hKey, "weight", NULL, REG_DWORD, (CONST BYTE *)dwWeight, 4); if (dwRet != ERROR_SUCCESS){ MessageBox("写入注册表失败"); return; }
::RegCloseKey(hKey); }
|
然后跑起来看看注册表里面有没有我们写入的。
md,::RegCreateKey(HKEY_LOCAL_MACHINE, "SOFTWARE\\MYWEIGHT\\admin", &hKey);
这个注册表位置管理员打开的软件居然写不进去,我找了半天没找到,换了个地方写就马上见效,离谱,浪费我一堆时间啊。
然后读的部分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| void CMyCFileDlg::OnBnClickedReadFile(){ HKEY hKey; DWORD dwRet = ::RegOpenKey(HKEY_CURRENT_USER, "Software\\HHH\\admin", &hKey); if (dwRet != ERROR_SUCCESS){ MessageBox("打开注册表失败"); return; }
DWORD dwWight; DWORD dwType; DWORD dwSize; CString strShow; dwRet = ::RegQueryValueExA(hKey, "weight", 0, &dwType, (LPBYTE) &dwWight, &dwSize); if (dwRet != ERROR_SUCCESS){ MessageBox("读取注册表失败"); return; } strShow.Format("Weight = %d", dwWight);
::RegCloseKey(hKey); MessageBox(strShow); }
|
其实写起来也不难,就是要填充这个参数问题。
效果就是这样了。
常规的文件等级
- 调试日志 debugview 文件日志、警告日志、错误日志 /五星
- 视频存储 /四星
- 文件传输CFile和Socket结合 /四星
- C语言和mfc的文件操作,win32api /三星
- windows的配置文件 /五星
- 注册表 病毒 逆向 /五星
结语
反正比较常用的文件操作还是以c语言和mfc为主吧,毕竟c用的很久了。mfc嘛自然在这个框架里面最好用。