:文件操作)
目录1. 为什么使用文件2. 什么是文件2.1 程序文件2.2 数据文件2.3 文件名3. 二进制文件和文本文件4. 文件的打开和关闭4.1 流和标准流4.2 文件指针4.3 fopen和fclose5. 文件的顺序读写5.1 fputc5.2 fgetc5.3 feof和ferror5.4 fputs5.5 fgets5.6 fprintf5.7 fscanf5.8 fwrite5.9 fread5.10 一组函数对比6. 总结1. 为什么使用文件如果没有文件程序的数据都存在内存中。程序一退出内存回收数据就丢了。下次运行程序看不到上次的数据。如果想把数据持久化保存就得用文件。就像写Word文档不保存成文件关机就没了。保存成.docx文件下次打开还能看到。2. 什么是文件磁盘硬盘上的文件就是文件。从功能角度分类程序设计中谈的文件有两种2.1 程序文件包括源程序文件.c目标文件Windows下.obj可执行程序Windows下.exe2.2 数据文件文件的内容不是程序而是程序运行时读写的数据。比如程序需要从中读取数据的文件或者程序输出内容的文件。本章讨论的就是数据文件。2.3 文件名一个文件要有唯一的文件标识文件路径 文件名主干 文件后缀 例如c:\code\test.txt为了方便文件标识常被称为文件名。3. 二进制文件和文本文件根据数据的组织形式数据文件分为文本文件和二进制文件。类型存储方式文本文件以ASCII字符形式存储需要转换二进制文件直接以内存中的二进制形式存储不加转换举个例子整数 10000以ASCII形式存储1 0 0 0 0占5个字节以二进制形式存储占4个字节#includestdio.hintmain(){inta10000;FILE*pffopen(test.txt,wb);// 以二进制写方式打开fwrite(a,4,1,pf);// 把a的4个字节写入文件fclose(pf);pfNULL;return0;}在VS中打开二进制文件可以看到10 27 00 00小端存储的10000。4. 文件的打开和关闭4.1 流和标准流流Stream可以把流想象成流淌着字符的河。C程序针对文件、画面、键盘等的数据输入输出操作都是通过流操作的。标准流C语言程序启动时默认打开了3个流流含义默认设备stdin标准输入流键盘stdout标准输出流显示器stderr标准错误流显示器这三个流的类型都是FILE*。所以我们用scanf、printf时不需要手动打开流。4.2 文件指针每个被使用的文件都在内存中开辟了一个文件信息区用来存放文件的相关信息。这些信息保存在一个结构体变量中类型由系统声明取名FILE。FILE*pf;// 文件指针变量通过文件指针可以间接找到与它关联的文件。pf1 ──→ 文件信息区 ──→ 文件1 pf2 ──→ 文件信息区 ──→ 文件2 pf3 ──→ 文件信息区 ──→ 文件34.3 fopen和fclosefopen —— 打开文件FILE*fopen(constchar*filename,constchar*mode);参数说明filename文件名可绝对路径或相对路径mode打开方式返回值成功返回FILE*指针失败返回NULL必须检查mode 打开方式一览方式含义文件不存在时r只读出错w只写建立新文件a追加建立新文件rb二进制只读出错wb二进制只写建立新文件ab二进制追加建立新文件r读写出错w读写建立新文件a读写建立新文件fclose —— 关闭文件intfclose(FILE*stream);成功返回0失败返回EOF。#includestdio.hintmain(){FILE*fpfopen(test.txt,r);if(fpNULL){perror(fopen);// 打印错误信息return1;}printf(打开文件成功\n);fclose(fp);fpNULL;// 置空避免野指针return0;}5. 文件的顺序读写函数功能适用于fgetc读取一个字符所有输入流fputc写入一个字符所有输出流fgets读取一个字符串所有输入流fputs写入一个字符串所有输出流fscanf格式化读取所有输入流fprintf格式化写入所有输出流fread读取一块数据文件输入流fwrite写入一块数据文件输出流5.1 fputcintfputc(intcharacter,FILE*stream);将字符写入流中成功返回写入的字符失败返回EOF。#includestdio.hintmain(){FILE*fpfopen(test.txt,w);if(fpNULL){perror(fopen);return1;}// 写入 a~zfor(intcha;chz;ch){fputc(ch,fp);}fclose(fp);fpNULL;return0;}5.2 fgetcintfgetc(FILE*stream);从流中读取一个字符成功返回读取的字符遇到文件末尾或出错返回EOF。#includestdio.hintmain(){FILE*fpfopen(test.txt,r);if(fpNULL){perror(fopen);return1;}// 读取前10个字符并打印for(inti0;i10;i){intcfgetc(fp);fputc(c,stdout);// 输出到屏幕}fclose(fp);fpNULL;return0;}5.3 feof和ferror函数功能feof(fp)检测是否遇到文件末尾ferror(fp)检测是否发生读写错误为什么fgetc返回EOF时要用这两个函数区分因为EOF既可能是正常读到文件末尾也可能是读取发生错误需要用feof和ferror来区分。#includestdio.hintmain(){FILE*fpfopen(test.txt,r);if(fpNULL){perror(fopen);return1;}intc;while((cfgetc(fp))!EOF){fputc(c,stdout);}// 循环结束判断原因if(feof(fp))printf(\n遇到文件末尾\n);elseif(ferror(fp))printf(\n读取发生了错误\n);fclose(fp);fpNULL;return0;}5.4 fputsintfputs(constchar*str,FILE*stream);将字符串写入流中不包含结尾的\0。#includestdio.hintmain(){FILE*fpfopen(test.txt,w);if(fpNULL){perror(fopen);return1;}fputs(abc\0def,fp);// 遇到 \0 就停了fputs(hehe,fp);fclose(fp);fpNULL;return0;}文件内容abchehe5.5 fgetschar*fgets(char*str,intnum,FILE*stream);从流中读取字符串最多读取num-1个字符留一个给\0遇到换行符或文件末尾停止。#includestdio.hintmain(){FILE*fpfopen(test.txt,r);if(fpNULL){perror(fopen);return1;}chararr1[20]{0};fgets(arr1,sizeof(arr1),fp);printf(%s,arr1);fclose(fp);fpNULL;return0;}最佳实践用fgetssscanf替代scanf更安全charbuf[256]{0};fgets(buf,sizeof(buf),stdin);buf[strcspn(buf,\n)]\0;// 去掉换行符5.6 fprintfintfprintf(FILE*stream,constchar*format,...);和printf类似但可以输出到任意输出流文件、stdout、stderr。#includestdio.hstructStu{charname[30];intage;floatscore;};intmain(){structStub{zhangsan,20,85.5f};FILE*fpfopen(test.txt,w);if(fpNULL){perror(fopen);return1;}fprintf(fp,%s %d %.1f,b.name,b.age,b.score);fclose(fp);fpNULL;return0;}5.7 fscanfintfscanf(FILE*stream,constchar*format,...);和scanf类似但可以指定输入源文件、stdin。#includestdio.hstructStu{charname[30];intage;floatscore;};intmain(){structStus{0};FILE*fpfopen(test.txt,r);if(fpNULL){perror(fopen);return1;}fscanf(fp,%s %d %f,s.name,s.age,s.score);fprintf(stdout,%s %d %.1f\n,s.name,s.age,s.score);fclose(fp);fpNULL;return0;}5.8 fwritesize_tfwrite(constvoid*ptr,size_tsize,size_tcount,FILE*stream);以二进制形式将数据块写入文件。#includestdio.hintmain(){intdata[]{1,2,3,4,5};FILE*fpfopen(data.bin,wb);if(fpNULL){perror(fopen);return1;}fwrite(data,sizeof(int),5,fp);fclose(fp);fpNULL;return0;}5.9 freadsize_tfread(void*ptr,size_tsize,size_tcount,FILE*stream);从文件中读取数据块到内存。#includestdio.hintmain(){intdata[5]{0};FILE*fpfopen(data.bin,rb);if(fpNULL){perror(fopen);return1;}size_tnum_readfread(data,sizeof(int),5,fp);if(num_read!5){if(feof(fp))printf(到达文件末尾\n);elseif(ferror(fp))printf(读取错误\n);}else{for(inti0;i5;i){printf(Data[%d]: %d\n,i,data[i]);}}fclose(fp);return0;}5.10 一组函数对比函数针对功能scanf标准输入stdin格式化输入printf标准输出stdout格式化输出fscanf所有输入流格式化输入fprintf所有输出流格式化输出sscanf字符串从字符串提取格式化数据sprintf字符串将格式化数据转换成字符串6. 总结操作函数打开文件fopen关闭文件fclose读字符fgetc写字符fputc读字符串fgets写字符串fputs格式化读fscanf格式化写fprintf二进制读fread二进制写fwrite判断文件末尾feof判断错误ferror