滴水三期:day26.2-C文件操作的函数

一、文件操作

  • 文件分为二进制文件和文本文件,如果我们要使用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()

函数名调用形式功能返回值
fgetsfgets(str,n,fp)从fp指向的文件读入一个长度为(n-1)的字符串,存放到字符数组str中,存放在字符串空间也可以读成功,返回地址str,失败则返回NULL
fputsfputs(str,fp)把str所指向的字符串写到文件指针变量fp所指向的文件中输出成功,返回0;否则返回非0值
fgetcfgetc(fp)从fp指向的文件读入一个字符读成功,返回所读的字符,失败则返回文件结束标志EOF(即-1)
fputcfputc(ch,fp)把字符ch写到文件指针变量fp所指向的文件中输出成功,返回值就是输出的字符;输出失败,则返回EOF(即-1)

三、作业

  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;
    }
    
  2. 将内存中的数据存储到一个文件中,(.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;
    }
    
  3. 用十六进制文本编辑器,打开一个记事本的.exe文件(普通的文件),再打开在内存中的记事本进程(已经加载到内存中),记录下这两个文件的不同

    • 打开notepad.exe文件是此文件在硬盘上是什么样子;如果运行notepad.exe文件再用winhex打开,就是加载到内存中是什么样子

    • 先找到notepad.exe所在位置,使用winhex打开;再双击notepad.exe将程序运行起来,此时点击winhex上面的Tools选项–open ram(打开内存)–找到notepad–选择当中的notepad.exe,即可打开此时内存中的notepad.exe

      屏幕截图 2021-12-19 171500 image-20211219171557821

    • 现在对比两个有何不同:

      1. 在硬盘上的exe打开后首地址是从0开始的;但是正在运行时在内存中是从0x1000000开始的

        屏幕截图 2021-12-20 125511 image-20211220125633766
      2. 还可以发现上面一大段都是相同的,直到出现了空白区,接着在硬盘上的文件的内容起始地址从向后偏移0x400的位置开始;但是正在运行时的文件的内容在内存中是从偏移0x1000的位置开始

        屏幕截图 2021-12-20 125838 屏幕截图 2021-12-20 125818
      3. 如果再往后面找,发现又会出现断开的地方,就是空白区,一堆00。再接着又是一大段数据。在硬盘上的文件空白区比运行时在内存中的空白区小,但是两者的结构都是惊人的相似

      4. 最后会发现硬盘上的文件总大小 < 运行时的大小,感觉运行时的文件向被拉伸了一样

        • 运行时最后大小为0x12fff + 1字节:

          image-20230305022059162
        • 硬盘上大小为0x101ff + 1字节:

          image-20230305022202309
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值