【C语言】文件操作全解析 depth: 3 title: 目录一、认识文件从功能角度将文件分为两类程序文件程序文件是用于构成程序本身的文件包括源程序文件.c后缀目标文件Windows环境下为.obj后缀可执行程序Windows环境下为.exe后缀数据文件数据文件是程序运行时读写的数据载体比如程序需要读取的配置文件程序输出的日志文件存储用户信息的数据库文件本文重点讨论的是数据文件的操作。文件名的构成一个完整的文件名包含三个部分文件路径指示文件在磁盘中的位置文件名主干文件的核心标识文件后缀指示文件类型例如c:\code\test.txt中c:\code\是路径test是文件名主干.txt是后缀。二、文本文件与二进制文件根据数据的存储形式数据文件可分为文本文件和二进制文件它们的区别如下1. 存储方式差异二进制文件数据在内存中以二进制形式存储不加转换直接输出到外存文本文件数据在存储前被转换为ASCII码形式以字符形式存储2. 实例对比以整数10000为例文本文件存储占用5个字节每个字符一个字节存储内容是’1’,‘0’,‘0’,‘0’,0’的ASCII码二进制文件存储在VS2019环境下仅占用4个字节直接存储其二进制形式00000000 00000000 00100111 000100003. 二进制文件操作示例#includestdio.hintmain(){inta10000;FILE*pffopen(test.txt,wb);// 以二进制写入模式打开文件fwrite(a,4,1,pf);// 将a以二进制形式写入文件fclose(pf);pfNULL;return0;}在VS中查看二进制文件时需要使用二进制编辑器10000会显示为10270000。三、文件的打开与关闭1. 流的概念C语言通过流stream来操作各种外部设备包括文件。流可以想象成流淌着字符的河程序通过流与外部设备进行数据交换。C程序启动时会默认打开3个标准流stdin标准输入流通常对应键盘stdout标准输出流通常对应显示器stderr标准错误流通常对应显示器这就是为什么我们可以直接使用scanf和printf函数进行输入输出而无需手动打开流。2. 文件指针缓冲文件系统中通过文件指针FILE*类型来管理文件。每个被打开的文件在内存中都有一个对应的文件信息区结构体存储文件名、状态、当前位置等信息文件指针就指向这个结构体。定义文件指针的方式FILE*pf;// 声明一个文件指针变量3. 打开与关闭函数打开文件使用fopen函数FILE*fopen(constchar*filename,constchar*mode);关闭文件使用fclose函数intfclose(FILE*stream);4. 文件打开模式文件打开模式决定了对文件的操作权限常见模式如下文件使用方式含义如果指定文件不存在“r”只读打开文本文件用于输入出错“w”只写打开文本文件用于输出创建新文件“a”追加向文本文件末尾添加数据创建新文件“rb”只读打开二进制文件用于输入出错“wb”只写打开二进制文件用于输出创建新文件“ab”追加向二进制文件末尾添加数据创建新文件“r”读写打开文本文件用于读写出错“w”读写创建文本文件用于读写创建新文件“a”读写打开文本文件用于读写从末尾开始创建新文件注意只写模式下如果文件存在并且有数据会覆盖原数据5. 示例代码#includestdio.hintmain(){FILE*pFile;// 打开文件pFilefopen(myfile.txt,w);// 文件操作if(pFile!NULL){fputs(fopen example,pFile);// 关闭文件fclose(pFile);}return0;}四、文件的顺序读写顺序读写是指从文件的开头到结尾依次进行读写操作C语言提供了一系列函数实现这一功能1. 常用顺序读写函数函数名功能适用于fgetc字符输入函数所有输入流fputc字符输出函数所有输出流fgets文本行输入函数所有输入流fputs文本行输出函数所有输出流fscanf格式化输入函数所有输入流fprintf格式化输出函数所有输出流fread二进制输入文件fwrite二进制输出文件fgetcint fgetc ( FILE * stream );当读取成功返回字符对应ASCII码读取失败或者读取结束返回EOF本质是-1(整型)fputcint fputc ( int character, FILE * stream );fgetschar * fgets ( char * str, int num, FILE * stream );fputsint fputs ( const char * str, FILE * stream );freadsize_t fread ( void * ptr, size_t size, size_t count, FILE * stream );fwritesize_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );所有输出流是指不仅可以通过文件流也可以通过标准流屏幕键盘读写数据2. 函数对比输入函数scanf从标准输入流读取格式化数据fscanf从指定输入流标准流文件流等读取格式化数据sscanfint sscanf ( const char * s, const char * format, ...);从字符串中读取格式化数据输出函数printf向标准输出流输出格式化数据fprintf向指定输出流输出格式化数据sprintfint sprintf ( char * str, const char * format, ... );将格式化数据输出到字符串五、文件的随机读写随机读写允许程序直接定位到文件的任意位置进行操作主要通过以下函数实现fseek函数定位文件指针根据文件指针的当前位置和偏移量来定位指针intfseek(FILE*stream,longintoffset,intorigin);参数origin可以是SEEK_SET从文件开头开始计算SEEK_CUR从当前位置开始计算SEEK_END从文件末尾开始计算示例#includestdio.hintmain(){FILE*pFile;pFilefopen(example.txt,wb);fputs(This is an apple.,pFile);fseek(pFile,9,SEEK_SET);// 定位到第10个字符位置fputs( sam,pFile);// 从该位置开始写入fclose(pFile);return0;}// 最终文件内容为This is a sample.ftell函数获取当前位置偏移量返回文件指针相对于文件起始位置的偏移量longintftell(FILE*stream);示例计算文件大小#includestdio.hintmain(){FILE*pFile;longsize;pFilefopen(myfile.txt,rb);if(pFileNULL)perror(Error opening file);else{fseek(pFile,0,SEEK_END);// 定位到文件末尾sizeftell(pFile);// 获取偏移量即文件大小fclose(pFile);printf(Size of myfile.txt: %ld bytes.\n,size);}return0;}rewind函数重置文件指针将文件指针重新定位到文件开头voidrewind(FILE*stream);示例#includestdio.hintmain(){intn;FILE*pFile;charbuffer[27];pFilefopen(myfile.txt,w);for(nA;nZ;n)fputc(n,pFile);rewind(pFile);// 回到文件开头fread(buffer,1,26,pFile);fclose(pFile);buffer[26]\0;printf(buffer);// 输出ABCDEFGHIJKLMNOPQRSTUVWXYZreturn0;}六、文件读取结束的判定1.feof函数feof函数的作用是当文件读取结束时判断结束的原因是否是遇到文件尾。它并不能直接用来判断文件是否结束。2. 判断方式文本文件使用fgetc时判断返回值是否为EOF使用fgets时判断返回值是否为NULL示例#includestdio.h#includestdlib.hintmain(void){intc;// 注意必须是int类型以处理EOFFILE*fpfopen(test.txt,r);if(!fp){perror(File opening failed);returnEXIT_FAILURE;}// fgetc读取失败或遇到文件结束时返回EOFwhile((cfgetc(fp))!EOF)putchar(c);// 判断结束原因if(ferror(fp))puts(I/O error when reading);elseif(feof(fp))puts(End of file reached successfully);fclose(fp);}二进制文件使用fread时判断返回值是否小于实际要读取的个数示例#includestdio.henum{SIZE5};intmain(void){doublea[SIZE]{1.,2.,3.,4.,5.};FILE*fpfopen(test.bin,wb);// 二进制模式fwrite(a,sizeof*a,SIZE,fp);// 写入数组fclose(fp);doubleb[SIZE];fpfopen(test.bin,rb);size_tret_codefread(b,sizeof*b,SIZE,fp);// 读取数组if(ret_codeSIZE){puts(Array read successfully, contents: );for(intn0;nSIZE;n)printf(%f ,b[n]);putchar(\n);}else{// 错误处理if(feof(fp))printf(Error reading test.bin: unexpected end of file\n);elseif(ferror(fp)){perror(Error reading test.bin);}}fclose(fp);}七、文件缓冲区ANSI C标准采用缓冲文件系统系统会自动为每个正在使用的文件在内存中开辟一块文件缓冲区输出数据时先送到内存缓冲区缓冲区满后再一起写入磁盘输入数据时先从磁盘读取到缓冲区再从缓冲区送到程序数据区缓冲区的大小由C编译系统决定。#includestdio.h#includewindows.h// VS2019 WIN11环境intmain(){FILE*pffopen(test.txt,w);fputs(abcdef,pf);// 数据先存入输出缓冲区printf(睡眠10秒-此时打开test.txt文件无内容\n);Sleep(10000);printf(刷新缓冲区\n);fflush(pf);// 手动刷新缓冲区数据写入磁盘printf(再睡眠10秒-此时打开test.txt文件有内容\n);Sleep(10000);fclose(pf);// 关闭文件时也会刷新缓冲区pfNULL;return0;}由于缓冲区的存在操作文件时必须注意及时刷新缓冲区使用fflush函数操作结束后关闭文件fclose会自动刷新缓冲区否则可能导致数据未能正确写入文件造成数据丢失或不一致。