目录
0x00.文件基本操作
0x01写入:
0x02获取文件大小:
0x03设置文件内容指针:
0x04拷贝文件:
0x05移动文件:
0x06关闭文件句柄:
0x07.文件夹操作
windows操作系统的文件系统:每个文件夹下都有两个默认隐藏的文件夹:
0x08实现文件夹遍历:
0x09.文件映射
C文件操作:FILE* fopen fwrite fread fseek ftell fclose
C++文件操作:fstream文件流对象 file.open file.wirte file.read file.seekg file.close
windows文件操作:不太常用,因为用C和C++的文件操作就可以了,前两者是跨平台的,在Linux上也可以使用。
用HANDLE句柄来标识文件
创建打开文件用 CreateFile
写文件 WriteFile
读文件 ReadFile
设置文件内容指针 SetFilePointer
拷贝文件 CopyFile()
获取文件大小 GetFileSize
移动文件 MoveFile
删除文件 DeleteFile
关闭文件 CloseHandle
当我们在写操作系统内核的代码 需要进行文件操作时,没得C和C++编译器供我们使用,只能用这些函数,所以必须掌握。
当一个函数你不知道怎么用的时候,vs编译器按F1或者F12查阅相关文档即可。
HANDLE CreateFileA(//返回创建或者打开后的文件句柄,如果失败,返回-1 LPCSTR lpFileName,//文件名 DWORD dwDesiredAccess,//访问方式 GENERIC_ALL 可读可写,GENERIC。。 只读 ,GENERIC。。只写 DWORD dwShareMode,//共享方式,指文件对于操作系统的很多用户的共享方式 LPSECURITY_ATTRIBUTES lpSecurityAttributes,//安全属性 SECURITY_开头 DWORD dwCreationDisposition,//创建(CREATE_AlWAYS) 还是 打开(OPEN_ALWAYS) DWORD dwFlagsAndAttributes,//方式和属性 HANDLE hTemplateFile//模板文件句柄 );CreateFile不仅仅可以用来打开或者创建一个文件,它可以用来打开或者创建一个设备(凡是可以和windows通信的都叫设备,包括文件(读写)、文件夹、逻辑磁盘驱动器(读写),物理磁盘驱动器(读写)、控制台(输入输出)等等)
windows为CreateFile打开的文件 创建一个文件内核对象,用这个内核对象来管理这个文件。多次调用CreateFile打开同一个文件,虽然是同一个文件,但是windows每次都会创建一个新的内核对象来管理它。每个内核对象中都有一个文件内容指针。纸箱应该读写的地址。
而所谓的文件句柄,不过是文件内核对象在进程句柄表的中的索引。
windows文件操作的好处:
1.可以创建隐藏文件:dwFlagsAndAttributes中如果选择FILE_ATTRIBUTE_HIDDEN可创建一个隐藏文件,这是C语言的文件操作所不能办到的。
2.windows文件操作是“线程安全的”,C或C++的文件操作则不是。所谓线程安全,就是当一个文件被当前进程所打开或者创建时,该文件就被该进程的该线程所占有,其他进程的线程无法读写该文件,直到该线程closehandle(即将对应文件的内核对象从该进程的句柄表中删除)或者该线程结束或者该进程结束。
3.很多其他操作:
获取文件大小拷贝文件移动文件// win32wenjian.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。 // #include "pch.h" #include <iostream> #include <windows.h> int main() { //1.使用该函数需要添加头文件 windows.h //2.该函数的作用: (1)如果同文件中没有该文件,创建该文件。如果有该文件,打开该文件(文件名处为路径名)。 (2)返回文件句柄给一个变量,方便操作文件。 HANDLE hFile=CreateFile("file.txt",GENERIC_ALL, FILE_SHARE_READ|FILE_SHARE_WRITE,/*共享方式 有 共享读,共享写,共享删除,全部以FILE_SHARE_开头*/ NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,//普通文件 NULL); if (INVALID_HANDLE_VALUE == hFile)//如果hFile == -1 即创建文件失败 { printf("创建文件失败!\n"); return -1; } printf("创建文件成功!\n"); while (1); return 0; }
DWORD 就是unsigned long 4个字节,2^32-1B=4 294 967 295B=4GB,即如果仅仅用返回值表示size的话,最大只能表示4GB,但是一个文件可能比4GB大。所以专门设置了一个参数:文件大小的高字节fileSizeHigh,将文件大小分成两个变量来存。低字节存在size中,高字节部分存在另一个变量中。
注意:打开文件和创建文件的 参数不同,CREATE_ALWAYS OPEN_ALWAYS
//得到文件 HANDLE hMkv = CreateFile("123.mkv", GENERIC_ALL, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hMkv == INVALID_HANDLE_VALUE) { cout << "打开失败!" << endl; return 0; } DWORD fileSizeHigh; DWORD size = GetFileSize(hMkv,&fileSizeHigh); long long totalSize = size | (long long)fileSizeHigh << 32; printf("文件大小为%ld字节\n", totalSize);名称为.的文件夹:表示当前文件夹
名称为..的文件夹:表示上层文件夹
思路:写一个函数 TravelDirectory(传入文件夹名),函数功能就是遍历一个文件夹。怎么遍历呢?当然是用递归来实现,最简单的情形的就是文件夹中没有文件,直接返回。那么如何缩小问题规模呢?当然是采用分治的思想来分布解决这个问题了。遍历一个文件夹可以分为两步,第一步、找到第一个文件,判断是文件还是文件夹,如果找到的是一个文件夹就调用TravelDirectory遍历。如果是文件,就输出文件名。第二步、不断找下一个文件。直到没有下一个文件就返回。
是不是感觉这个思路 和 深度寻路算法很相似呢?其实问题的本质就是深度寻路算法的问题。只不过,遍历文件夹,问题更简单,因为windows的所有文件其实就是一个森林,每一个盘符都是一个树。所以我们不需要像深度遍历图一样,准备一个辅助数组来记录每个点是不是已经走过。
下面我们来详细讨论每一步具体怎么做:
1.通配符号:
%dint\n换行\\\%%%*对应任意个任意字符?对应一个任意字符2.找指定文件(从头开始找):
注意:并不是像函数名说的找第一个文件。这个函数就是找指定的文件。
如果能找到指定文件,创建对应的文件对象,返回该文件对象在当前进程的句柄。如果找不到指定名字的文件,返回值为INVALID_HANDLE_VALUE.
HANDLE FindFirstFileA( LPCSTR lpFileName, //你要找的文件的名字 LPWIN32_FIND_DATAA lpFindFileData //你找到的文件的相关信息,根据属性可以判断是文件还是文件夹 );使用实例:
WIN32_FIND_DATA fileInfo; HANDLE hFile = FindFirstFile("123.mkv", &fileInfo); if (INVALID_HANDLE_VALUE == hFile) { cout << "找不到" << endl; return 0; } cout << "找到了" << endl;我们用一个结构体来存该文件的相关信息,WIN32_FIND_DATA结构体定义如下:
typedef struct _WIN32_FIND_DATAW { DWORD dwFileAttributes;//文件属性 FILETIME ftCreationTime;//创建时间 FILETIME ftLastAccessTime;//最后一次访问时间 FILETIME ftLastWriteTime;//最后一次写入时间 DWORD nFileSizeHigh;//文件大小 DWORD nFileSizeLow;//文件大小 DWORD dwReserved0;//保留位,更新迭代之用 DWORD dwReserved1;//保留位,更新迭代之用 _Field_z_ WCHAR cFileName[ MAX_PATH ];//文件名 _Field_z_ WCHAR cAlternateFileName[ 14 ]; } WIN32_FIND_DATAW, *PWIN32_FIND_DATAW, *LPWIN32_FIND_DATAW;其中文件属性包括:
#define FILE_ATTRIBUTE_READONLY 0x00000001 #define FILE_ATTRIBUTE_HIDDEN 0x00000002 #define FILE_ATTRIBUTE_SYSTEM 0x00000004 #define FILE_ATTRIBUTE_DIRECTORY 0x00000010 #define FILE_ATTRIBUTE_ARCHIVE 0x00000020 #define FILE_ATTRIBUTE_DEVICE 0x00000040 #define FILE_ATTRIBUTE_NORMAL 0x00000080 #define FILE_ATTRIBUTE_TEMPORARY 0x00000100 #define FILE_ATTRIBUTE_SPARSE_FILE 0x00000200 #define FILE_ATTRIBUTE_REPARSE_POINT 0x00000400 #define FILE_ATTRIBUTE_COMPRESSED 0x00000800 #define FILE_ATTRIBUTE_OFFLINE 0x00001000 #define FILE_ATTRIBUTE_NOT_CONTENT_INDEXED 0x00002000 #define FILE_ATTRIBUTE_ENCRYPTED 0x00004000 #define FILE_ATTRIBUTE_INTEGRITY_STREAM 0x00008000 #define FILE_ATTRIBUTE_VIRTUAL 0x00010000 #define FILE_ATTRIBUTE_NO_SCRUB_DATA 0x00020000 #define FILE_ATTRIBUTE_EA 0x00040000 #define FILE_ATTRIBUTE_PINNED 0x00080000 #define FILE_ATTRIBUTE_UNPINNED 0x00100000 #define FILE_ATTRIBUTE_RECALL_ON_OPEN 0x00040000 #define FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS 0x00400000通过这个函数,我们只要将文件名设置为“传入的文件夹名\\*”,就可以找到文件夹中的第一个任意文件。
3.找下一个文件:给定一个文件对象句柄,找到文件夹中该文件的下一个文件。如果能找到下一个文件,返回值为真,如果找不到下一个文件,返回值为假。我们遍历文件夹就是一个循环,我们可以用这个函数来作为循环结束的条件。
BOOL WINAPI FindNextFileA( _In_ HANDLE hFindFile, _Out_ LPWIN32_FIND_DATAA lpFindFileData );源代码:无注释版。这个代码经过修改,可以查找磁盘中特定后缀的文件。并进行特定操作。
// 20-windows文件操作.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。 // #include "pch.h" #include <iostream> #include <Windows.h> using namespace std; void travelDirectory(char* pathName) { TCHAR fileName[MAX_PATH] = { 0 }; sprintf(fileName, "%s\\*", pathName); WIN32_FIND_DATA fileInfo; HANDLE hFile = FindFirstFile(fileName, &fileInfo); if (INVALID_HANDLE_VALUE == hFile) { //cout << "空文件夹" << endl; return; } int ret = 1; TCHAR tempName[MAX_PATH] = { 0 }; while (ret) { if (fileInfo.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY) { if (fileInfo.cFileName[0] != '.') {//跳过".文件夹和..文件夹" memset(tempName, 0, sizeof(TCHAR)*MAX_PATH); sprintf(tempName, "%s\\%s", pathName, fileInfo.cFileName); //printf("文件夹:%s\n", tempName); travelDirectory(tempName); } } #if 0 else if (fileInfo.dwFileAttributes == FILE_ATTRIBUTE_NORMAL) { printf("文件:%s\n", fileInfo.cFileName); } #endif else if(fileInfo.dwFileAttributes == FILE_ATTRIBUTE_HIDDEN){ printf("隐藏文件:%s\n", fileInfo.cFileName); } #if 1 else { printf("其他文件:%s\n", fileInfo.cFileName); } #endif ret = FindNextFile(hFile, &fileInfo); } return; } int main() { travelDirectory((char*)"F:"); cout << "遍历完成" << endl; return 0; }注释版:
// win32文件夹操作.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。 // #include "pch.h" #include <iostream> #include <windows.h>//用windows的宏都要加这个头文件 int count = 0; void findFile(char* fileName) { //1.设置文件名 fileName//*.* char findFileName[MAX_PATH] = { 0 }; //define MAX_PATH 260 在windows操作系统上,一个文件的名字,最大260个字节 sprintf(findFileName, "%s\\*.*", fileName);// 反斜杠需要转义 *.*也可以修改为* /* Windows中常用的通配符是 * 可以代替所有的字母/中文 ? 可以代替一个字母/中文 */ //2.循环找文件 WIN32_FIND_DATA findData;//结构体,用来专门装文件的属性 HANDLE hFile = FindFirstFile(findFileName, &findData);// if (INVALID_HANDLE_VALUE == hFile) {//如果没有找到第一个文件 return; } int ret = 1; char temp[MAX_PATH]; while (ret) {//找不到为止 //2.1判断是文件夹还是文件 if (FILE_ATTRIBUTE_DIRECTORY == findData.dwFileAttributes) { //2.1.1文件夹 if (findData.cFileName[0] != '.')//防止陷入 .文件夹 和 ..文件夹的死循环里面 { //2.1.1.1设置文件名 memset(temp, 0, MAX_PATH); sprintf(temp, "%s\\%s", fileName, findData.cFileName); printf("%d:%s\n", count++, temp); Sleep(1000); //2.1.1.2遍历文件夹 findFile(temp); } } else { //2.1.2文件 //打印文件名 或者do someting else memset(temp, 0, MAX_PATH); sprintf(temp, "%s\\%s", fileName, findData.cFileName); printf("%d:%s\n", count++, temp); Sleep(1000); } //2.2找下一个文件 ret = FindNextFile(hFile, &findData);//如果找到了下一个文件,返回1,没找到返回0 } } int main() { //获取当前文件夹名 char currentDirName[MAX_PATH] = { 0 }; GetCurrentDirectory(MAX_PATH,currentDirName);//获取当前文件夹名称,储存在一个字符数组中 findFile(currentDirName); } //作业: //代码行数查看器。 //找到一个文件夹下所有的.c .cpp文件统计一共写了多少行代码物理内存:内存条。
虚拟内存:物理内存映射而来。
io操作:常规的文件操作方式,需要从磁盘中读取或者写入磁盘
内存操作:CPU直接读写内存,效率更高
可以像操作内存来操作内存,因为文件io(读写)很慢。
选做:创建一个保存有4G个电话号码的文件,查找某个电话号码。
方式1.直接读文件
方式2.映射文件为虚拟内存,内存操作。