一、文件操作
-
文件分为二进制文件和文本文件,如果我们要使用C语言来操作一个文件,需要先将文件以数据流的形式打开,因为一个程序与数据的交互是以流的形式进行的;接着将文件数据读入内存,可以通过指向文件数据的文件指针对文件中的数据进行操作,最后要关闭数据流
-
文件类型指针:
FILE*typedef struct { short level; //缓冲区“满”或“空”的程度 unsigned flags; //文件状态标志 char fd; //文件描述符 unsigned char hold; //如缓冲区无内容不读取字符 short bsize; //缓冲区的大小 unsigned char*buffer; //数据缓冲区的位置 unsigned char*curp; //文件位置标记指针当前的指向 unsigned istemp; //临时文件指示器 short token; //用于有效性检查 }FILE;
二、文件操作的函数
1.fopen()
-
格式:
FILE* fp = fopen("文件名","打开模式");文件名可以填相对地址或者绝对地址,绝对地址格式为:
D:\\father\\son.exe,或者D:/father/son.exe;打开模式简单分为:-
“r”:只能从文件中读数据,该文件必须先存在,否则打开失败
-
“w”:只能向文件写数据,若指定的文件不存在则创建它,如果存在则先删除它再重建一个新文件
-
“a”:向文件增加新数据(不删除原有数据),若文件不存在则打开失败,打开时位置指针移到文件末尾
-
“rb”、“wb”、“ab”:表示以二进制形式打开的文件
-
-
举例:
FILE* fp = fopen("e:\zjj\a.txt","r"); -
返回值说明:如果正常打开,则返回被打开文件的文件指针;如果打开异常,则返回NULL
2.fclose()
-
格式:
int n = fclose(fp);fp表示一个已经被打开文件的文件指针
-
举例:
FILE* fp = fopen("d:\a.txt","w"); int n = fclose(fp); -
返回值说明:如果正常关闭则返回0;如果异常则返回EOF
3.fwrite()和fread()
==二进制读写==文件函数
-
格式:
size_t fread(要读的内存指针,读的这内存大小,有几个这样的内存,文件指针); //从文件中读 size_t fwrite(要写的内存指针,写的这内存大小,有几个这样的内存,文件指针); //向文件中写 //如:假设已经定义好了一个Student结构体,一般是以一个结构体为单位的方式读写,自己定义大小也行 Student stu; fread(&stu,sizeof(Student),1,fp); //或者一直需要读取100字节数据到内存 char* mem = 某个内存起始地址; fread(mem,100,1,fp); -
返回值:成功读写的字节数
4.fseek()
在文件中定位的函数
-
格式:
int i = fseek(文件指针,偏移量,基准);-
第一个参数fp为文件指针;
-
第二个参数是偏移量,表示要偏移的字节数,正数表示正向偏移,负数表示负向偏移;
-
第三个参数表示设定从文件的哪里开始偏移,取值为:0、1、2
-
为0:表示从文件起始位置增加 offset 个偏移量为新的读写位置
-
为1:表示从目前的读写位置增加 offset 个偏移量为新的读写位置
-
为2:表示从文件尾后,再增加 offset 个偏移量为新的读写位置
-
-
-
作用:完成文件的定位,用来设定文件的当前读写位置
-
举例:
FILE* fp = fopen("d:\a.txt","wb"); //假设文件内容为abcd int i = fseek(fp,sizeof(char)*2,0); //fp指向从a开始向后偏移2字节位置,即c。但是i的值为0 //或者 int i = fseek(fp,100L,1); -
返回值说明:如果执行成功,返回值为0;如果执行失败,不改变 fp 指向的位置,函数的返回值为 -1
5.ftell()
在文件中定位的函数
-
格式:
long 或 int lenth = ftell(文件指针);一般都是用在fseek函数后面,以为fseek函数只显示移动的成功与否,不显示移动后指针的位置,所以用ftell来返回指针移动后的位置。
-
作用:使用 ftell 函数来取得当前文件的读写位置(是相对于传入的文件来说的相对位置,比如==最开始ftell返回的值为1==),以字节为单位!
-
要注意:
FILE* fp = fopen("d:\a.txt","rb"); //假如a.txt文件中的内容是123,即3字节 int start = ftell(fp); //最开始指向的位置应为1!!! fseek(fp,0,2); //把fp移到文件尾 int size = ftell(fp); //此时size的值应该为3!!!而不是2!!一定要注意!所以可以用此方法算文件大小
6.fprintf()和fscanf()
-
格式:
fscanf(fp,"%d",&num); //第一个参数是文件指针,后面两个和scanf参数一样 //fprintf同理 -
fscanf:从文件指针中读取数据,存储到后面的参数中
-
fprintf:把后面的参数中的数据,存储到文件指针中去
-
每读或写完:文件指针都要向后偏移
7.fgetc()、fputc()和fgets()、fputs()
| 函数名 | 调用形式 | 功能 | 返回值 |
|---|---|---|---|
| fgets | fgets(str,n,fp) | 从fp指向的文件读入一个长度为(n-1)的字符串,存放到字符数组str中,存放在字符串空间也可以 | 读成功,返回地址str,失败则返回NULL |
| fputs | fputs(str,fp) | 把str所指向的字符串写到文件指针变量fp所指向的文件中 | 输出成功,返回0;否则返回非0值 |
| fgetc | fgetc(fp) | 从fp指向的文件读入一个字符 | 读成功,返回所读的字符,失败则返回文件结束标志EOF(即-1) |
| fputc | fputc(ch,fp) | 把字符ch写到文件指针变量fp所指向的文件中 | 输出成功,返回值就是输出的字符;输出失败,则返回EOF(即-1) |
三、作业
-
将记事本的.exe文件读取到内存,并返回读取后在内存中的地址
- 思路:
- 打开要读取的文件,二进制读的形式
- 计算文件内容的长度(字节为单位)
- 动态申请内存,大小要够存放此文件内容
- 将文件内容写入申请的内存
- 返回申请内存的起始地址,即文件写入内存的地址
#include "stdafx.h" #include "stdlib.h" //文件操作函数应该在stdio.h中,但是stdlib.h中包含了 char* Function(char* filePath){ FILE* fp = fopen(filePath,"rb"); //以二进制读的方式打开此文件 if(fp == NULL){ exit(0); //异常处理 } fseek(fp,0,2); //定位到文件结尾 int size = ftell(fp); //显示指针当前的位置,即文件结尾的位置,它是按偏移字节显示的,所以这个值相当于文件的大小(字节) char* p = (char*)malloc(size); //动态申请内存,大小为size if(!p){ exit(0); //异常处理 } char* ret = p; //先将此时申请内存的起始地址记住,后面要返回这个值 fseek(fp,0,0); //将文件指针重新定位到文件的起始位置 //方法一:用fread fread(p,size,1,fp); /*方法二:用fscanf for(int i = 0;i < size;i++){ fscanf(fp,"%c",(p + i)); //一个字节一个字节将文件的内容写入内存中 }*/ /*方法三:用fgetc for(int i = 0;i < size;i++){ *(p + i) = fgetc(fp); }*/ /*方法四:用fgets fgets(p,size,fp); */ fclose(fp); //最后关闭流 free(p); return ret; //返回文件在内存中的地址 } int main(int argc,char* argv){ char* filePath = "D:/C-language/file/notepad.exe"; //这里填写绝对路径即可 char* pMem = Function(filePath); printf("%x",pMem); return 0; } - 思路:
-
将内存中的数据存储到一个文件中,(.exe格式),然后双击打开,看是否能够使用
- 思路:
- 在第一题的基础上,只需要再打开一个文件,fopen如果发现没有此文件,则会新建
- 将内存中的数据写入到此文件中即可
#include "stdafx.h" #include <stdlib.h> //将内存中的数据存储到一个文件中(.exe格式),然后双击打开 void memToFile(char* filePath,char* newFilePath){ //先把notepad.exe数据读入到内存中 FILE* fp = fopen(filePath,"rb"); fseek(fp,0,2); int fileSize = ftell(fp); char* pMem = (char*)malloc(fileSize); char* start = pMem; //记录一下分配空间的首地址 fseek(fp,0,0); fread(pMem,fileSize,1,fp); //再把内存中的数据写入notepad1.exe文件中 FILE* newfp = fopen(newFilePath,"wb"); fwrite(start,fileSize,1,newfp); fclose(fp); fclose(newfp); free(pMem); printf("success"); } int main(int argc,char* argv[]){ char* filePath = "D:/C-language/file/notepad.exe"; char* newFilePath = "D:/C-language/file/notepad1.exe"; memToFile(filePath,newFilePath); getchar(); return 0; } - 思路:
-
用十六进制文本编辑器,打开一个记事本的.exe文件(普通的文件),再打开在内存中的记事本进程(已经加载到内存中),记录下这两个文件的不同
-
打开notepad.exe文件是此文件在硬盘上是什么样子;如果运行notepad.exe文件再用winhex打开,就是加载到内存中是什么样子
-
先找到notepad.exe所在位置,使用winhex打开;再双击notepad.exe将程序运行起来,此时点击winhex上面的Tools选项–open ram(打开内存)–找到notepad–选择当中的notepad.exe,即可打开此时内存中的notepad.exe

-
现在对比两个有何不同:
-
在硬盘上的exe打开后首地址是从0开始的;但是正在运行时在内存中是从0x1000000开始的
-
还可以发现上面一大段都是相同的,直到出现了空白区,接着在硬盘上的文件的内容起始地址从向后偏移0x400的位置开始;但是正在运行时的文件的内容在内存中是从偏移0x1000的位置开始
-
如果再往后面找,发现又会出现断开的地方,就是空白区,一堆00。再接着又是一大段数据。在硬盘上的文件空白区比运行时在内存中的空白区小,但是两者的结构都是惊人的相似
-
最后会发现硬盘上的文件总大小 < 运行时的大小,感觉运行时的文件向被拉伸了一样
-
运行时最后大小为0x12fff + 1字节:
-
硬盘上大小为0x101ff + 1字节:
-
-
-

被折叠的 条评论
为什么被折叠?



