在程序设计中文件有两种,一种是程序文件(包括源程序文件(后缀为.c),目标文件(windows环境后缀为.obj),可执行程序(windows环境后缀为.exe)),另外一种就是数据文件(文件的文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件, 或者输出内容的文件),也就是本篇文章所介绍的东东啦。
目录
文件名
文件类型
流
文件的打开和关闭
1.文件指针
2.fopen
3.fclose
文件的顺序读写
1.fputc
2.fgetc
3.fputs
4.fgets
5.fprintf
6.fscanf
7.fwrite
8.fread
对比printf,fprintf,,sprintf
文件的随机读写
1.fseek
2.ftell
3.rewind
文件读写结束的判断
一个文件要有一个唯一的文件标识,以便用户识别和引用,而我们通常称文件标识为文件名。文件名包含3 部分: 文件路径 + 文件名主干 + 文件后缀 例如: (c:\code\test.txt)注意文件名不能包含一些特殊字符: \ / : * ? " < > |
数据文件根据数据的组织形式还可以分为文本文件和二进制文件。
二进制文件:数据在内存中以二进制的形式存储,如果不加转换的输出到外存就是二进制文件
文本文件:如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的文件就是文本文件
流是个抽象的概念,是对输入输出设备(文件,网络,内存等)的抽象,对于数据的输入/输出操作都是以“流”的方式进行。当程序需要从某个数据源读入数据的时候,就会开启一个输入流,数据源可以是文件、内存或网络等等。相反地,需要写出数据到某个数据源目的地的时候,也会开启一个输出流,这个数据源目的地也可以是文件、内存或网络等等。
任何一个C程序,只有运行起来就会默认打开3个流:FILE* stdin(标准输入流:对应键盘)
FILE* stdout(标准输出流:对应屏幕)FILE* stderr(标准错误流:应屏幕)
在对文件进行读写操作前后分别需要对文件进行打开和关闭的操作。其操作离不开文件指针。
1.文件指针
文件指针(如:FILE* pf)是用来维护文件的。每个文件被使用的时候都会在内存中开辟了一个相应的文件信息区,文件信息区存放有文件的相关信息(如文件的名字,文件状态及文件当前的位置等)。这些信息会被保存在一个名为FILE的结构体变量中,通过一个FILRE的指针可以维护该结构体,进而访问对应的文件。2.fopen
fopen函数是用来打开文件的函数,
它的返回值FILE*,即打开文件的同时返回一个FILE*的指针变量指向该文件,也相当于建立了文件和指针的关系。第一个参数(const char* filename)为字符串,包含要打开的文件的名称;第二个参数(const char* mode)为模式说明符(文件的打开方式),代表文件的读写权限。
第二个参数文件打开方式最基本的有以下几种方式:
调用 fopen() 函数时必须指明读写权限,但是可以不指明读写方式(此时默认为
控制读写权限的字符串(必须指明) 打开方式 说明 "r" r(read),以“只读”方式打开文件。只允许读取,不允许写入。文件必须存在,否则打开失败。 "w" w(write),以“写入”方式打开文件。如果文件不存在,那么创建一个新文件;如果文件存在,那么清空文件内容(相当于删除原文件,再创建一个新文件)。 "a" a(append),以“追加”方式打开文件。如果文件不存在,那么创建一个新文件;如果文件存在,那么将写入的数据追加到文件的末尾(文件原有的内容保留)。 "r+" 以“读写”方式打开文件。既可以读取也可以写入,也就是随意更新文件。文件必须存在,否则打开失败。 "w+" 以“写入/更新”方式打开文件,相当于 w
和r+
叠加的效果。既可以读取也可以写入,也就是随意更新文件。如果文件不存在,那么创建一个新文件;如果文件存在,那么清空文件内容(相当于删除原文件,再创建一个新文件)。"a+" 以“追加/更新”方式打开文件,相当于a和r+叠加的效果。既可以读取也可以写入,也就是随意更新文件。如果文件不存在,那么创建一个新文件;如果文件存在,那么将写入的数据追加到文件的末尾(文件原有的内容保留)。 控制读写方式的字符串(可以不写) 打开方式 说明 "t" t(text),文本文件。如果不写,默认为 "t"
。"b" b(binary),二进制文件。 "t"
)。
读写权限和读写方式可以组合使用,但是必须将读写方式放在读写权限的中间或者尾部(换句话说,不能将读写方式放在读写权限的开头)。例如:1.将读写方式放在读写权限的末尾:"rb"、"wt"、"ab"、"r+b"、"w+t"、"a+t"
2.将读写方式放在读写权限的中间:"rb+"、"wt+"、"ab+“
注:使用fopen函数需要判断文件是否打开成功,若打开失败则返回空指针
3.fclose
fclose函数是用来关闭文件的,
它的返回值为int,如果成功关闭,则返回零值,关闭失败,返回 EOF。返回参数(FILE* stream)为要关闭的文件的 FILE 对象的指针。注意关闭文件后最好将文件指针置空。
下面是文件的打开和关闭的操作样例
#include
int main() { FILE* pf = fopen("test.txt","w");//打开文件 if (pf == NULL)//判断打开成功与否 { perror("FILE:"); return 1; } //进行读写操作 //... //结束读写操作 fclose(pf);//关闭文件 pf = NULL; return 0; }
1.fputc
fputc函数在文件的读写中用于把一个字符写入指定的文件中,
返回类型为int,写入成功则返回写入的字符,写入错误则返回 EOF 。第一个参数(int character)为要写入的字符(写入时,该值在内部转换为无符号字符)。第二个参数(FILE* stream)为指向要访问的文件的文件指针。
下面是fgetc函数的使用样例:
#include
int main() { FILE* pf = fopen("test.txt", "w");//以写方式打开文件 if (pf == NULL)//判断打开成功与否 { perror("FILE:"); return 1; } fputc("a", pf)//将字符a写入pf所指向的文件中 fclose(pf);//关闭文件 pf = NULL; return 0; } 2.fgetc
fgetc函数在文件的读写中用于从指定的文件中读一个字符,
返回类型为int,成功后返回读取的字符(提升为 int 值),失败返回EOF;函数参数(FILE* stream)为指向要访问的文件的文件指针。
下面是fgetc函数的使用样例:
#include
int main() { FILE* pf = fopen("test.txt", "r");//以读方式打开文件 if (pf == NULL)//判断打开成功与否 { perror("FILE:"); return 1; } while(ch=fgetc(pf)!=EOF)//读取pf所值向的文件内容 { printf("%c",ch);//打印读到的内容 } fclose(pf);//关闭文件 pf = NULL; return 0; } 3.fputs
fputs函数在文件的读写中用于向指定的文件写入一个字符串,
返回类型为int,写入成功则返回非负值,写入错误则返回 EOF 。第一个参数(const char* str)为要写入的字符串的起始地址。第二个参数(FILE* stream)为指向要访问的文件的文件指针。
下面是fputs函数的使用
样例:
#include
int main() { FILE* pf = fopen("test.txt", "w");//以写方式打开文件 if (pf == NULL)//判断打开成功与否 { perror("FILE:"); return 1; } fputs("abc",pf);//将字符串abc写入pf所指向的文件中 fclose(pf);//关闭文件 pf = NULL; return 0; } 4.fgets
fgets函数在文件的读写中用于从文件中读取字符并将其作为 C 字符串存储到 str 中,直到读取 (num-1) 字符或达到换行符或文件末尾(以先发生者为准)。(注:1.换行符会使 fgets 停止读取,但它被函数视为有效字符,并包含在复制到 str 的字符串中 。 2.终止空字符也会自动追加到复制到 str 的字符之后。)
返回类型为char*,读取成功返回指向在其中复制字符串读取的数组的指针,读取失败返回空指针。第一个参数(char* str)为指向在其中复制字符串读取的数组的指针;第二个参数(int num)为要复制到 str 中的最大字符数(包括终止空字符);第三个参数(FILE* stream)为指向要访问的文件的文件指针。下面是fgets函数的使用样例:
#include
int main() { FILE* pf = fopen("test.txt", "r");//以读方式打开文件 if (pf == NULL)//判断打开成功与否 { perror("FILE:"); return 1; } char arr[10] = { 0 }; fgets(arr,5,pf); //进行读操作 printf("%s\n", arr); fclose(pf);//关闭文件 pf = NULL; return 0; } 5.fprintf
fprintf在文件的读写中用于将格式化的数据写入文件,
fprint的用法可以类比printf的用法
对比一下可以发现fprintf比printf就多加了一个参数 (FILE* stream),这样我们很容易可以得到下面fprintf的使用样例:
#include
struct a { int m; char p; }a1={2,‘a’}; int main() { FILE* pf = fopen("test.txt", "w"); if (pf == NULL) { perror("FILE:"); return 1; } fprintf(pf, "%d", a1.m);//比printf多一个参数pf fclose(pf); pf = NULL; return 0; } 结合流的内容,printf("%d",a);也可以写成fprintf(stdout,"%d",a);。
6.fscanf
fscanf在文件的读写中用于从文件中读取数据,并根据参数格式将其存储到附加参数所指向的位置
fscanf的用法也可以类比scanf的用法
对比一下同样发现fscanf比scanf就多加了一个参数 (FILE* stream),这样我们很容易可以得到下面fprintf的使用样例:
#include
struct a { int m; char p; }a1; int main() { FILE* pf = fopen("test.txt", "r"); if (pf == NULL) { perror("FILE:"); return 1; } fscanf(pf, "%d", &a1.m);//比scanf多一个参数pf printf("%d\n",a1.m); fclose(pf); pf = NULL; return 0; } 7.fwrite
fwritez函数在文件的读写中用于将数据块以二进制的形式写到文件中。
返回值为成功写入的元素总数,如果该数字与 count 参数不同,则会出现写入错误。第一个参数(const void* ptr)为指向要写入的元素数组的指针;第二个参数(size_t size)为要写入的每个元素的大小(以字节为单位);第三个参数(size_t count)为要写入的元素数,每个元素的大小为size字节;第四个参数(FILE* stream)为指向要访问的文件的文件指针。
下面是fwrite函数的使用样例:
#include
struct S { char arr[10]; int a; }; int main() { struct S s = { "abc",21 }; FILE* pf = fopen("text.txt", "wb"); if (pf == NULL) { perror("FILE:"); return 1; } fwrite(&s, sizeof(struct S), 1, pf); fclose(pf); pf = NULL; return 0; } 8.fread
fread函数在文件的读写中用于以二进制的形式读取数据。
返回值为成功读取的元素总数,如果该数字与 count 参数不同,或者读到文件末尾,则会出现写入错误。第一个参数(void* ptr)为值向大小至少为(大小*计数)字节的内存块的指针,转换为void*;第二个参数(size_t size)为要读取的每个元素的大小(以字节为单位);第三个参数(size_t count)为要读取的元素数,每个元素的大小为size字节;第四个参数(FILE* stream)为指向要访问的文件的文件指针。
下面是fwrite函数的使用样例:
#include
struct S { char arr[10]; int a; }; int main() { struct S s = { "abc",21 }; FILE* pf = fopen("text.txt", "wb"); if (pf == NULL) { perror("FILE:"); return 1; } fread(&s, sizeof(struct S), 1, pf); printf("%s %d\n", s.arr, s.a); fclose(pf); pf = NULL; return 0; }
printf,scanf和fscanf,fprintf我们都见识过了,那么这里就再介绍两个函数:sprintf和sscanf
sprintf
sprintf函数用于把一个格式化的数据写到字符串中,
用法和fprintf差不多,只是把文件指针换成字符指针,废话就不多说了,直接上样例:
#include
int main() { struct S s = { "abc",3 }; char buf[100]; sprintf(buf,"%s %d", s.arr, s.n); printf("%s", buf);//打印结果为abc 3 return 0; } sscanf
sscanf函数是用于从一个字符串中转化出一个格式化的数据,看样用法样例:
struct S { char arr[10]; int n; }; #include
int main() { struct S s = { 0 }; char buf[10] = "abc 3"; sscanf(buf, "%s %d", s.arr, &s.n); printf("%s %d", s.arr, s.n);//打印结果为abc 3 return 0; } 总结:
scanf:是针对标准输入的格式化输入语句
printf:是针对标准输出的格式化输出语句
fscanf:是针对所有输入流的格式化输入语句
fprintf:是针对所有输出流的格式化输出语句
sscanf:是从一个字符串中转化出一个格式化的数据
sprintf:是把一个格式化的数据转化成字符串
1.fseek
fseek函数用于根据文件指针的位置和偏移量来定位文件指针。
返回类型为int,成功返回零,否则返回非零值。第一个参数(FILE* stream)为指向要访问的文件的文件指针;第二个参数(long int offset)若为二进制文件则是要从源偏移的字节数,若为文本文件则是零或 ftell 返回的值;第三个参数(int origin)用作偏移参考的位置,它由常量(SEEK_SET(文件开头),SEEK_CUR(文件指针的当前位置),SEEK_END(文件结尾))之一指定,这些常量在
专门用作此函数的参数 。2.ftell
ftell函数用于返回文件指针相对于起始位置的偏移量
返回类型为long int,成功则返回仓位指标的当前值,失败返回 -1L 。参数FILE* stream)为指向要访问的文件的文件指针
3.rewind
rewind函数用于让文件指针的位置回到文件的起始位置。就喜欢这种参数返回类型简单明了的函数,哈哈
再看看随机读写用法样例子:
#include
struct S { char arr[10]; int m; }s1[10], s2; int main() { struct S s1[10] = { {"abc",1},{"def",2},{"gew",3} }; FILE* pf = fopen("text.txt", "wb+"); if (pf == NULL) { perror("FILE"); return 1; } fwrite(s1, sizeof(struct S), 3, pf);//写入3条信息 fseek(pf, sizeof(struct S), SEEK_SET);//移动文件指针 fread(&s2, sizeof(struct S), 1, pf);//读取一条信息 printf("%s %d", s2.arr, s2.m);//打印结果为:def 2 fclose(pf); pf = NULL; return 0; }
1. 文本文件读取是否结束,判断返回值是否为 EOF ( fgetc ),或者 NULL ( fgets )例如:fgetc 判断是否为 EOF .fgets 判断返回值是否为 NULL .int main() { FILE* pf = fopen("text.txt", "r"); if (pf == NULL) { perror("FILE:"); return 1; } char ch = 0; while ((ch = fgetc(pf)) != EOF)/*fgetc 当读取失败的时候或者遇到文件结束的时候,都会返回EOF*/ { putchar(ch); } //判断是什么原因结束的 if (ferror(pf)) { puts("I/O error when reading"); } else if(feof(pf)) { puts("End of file reached successfully"); } fclose(pf); pf = NULL; return 0; }
2. 二进制文件的读取结束判断,判断返回值是否小于实际要读的个数。例如:fread 判断返回值是否小于实际要读的个数。#include
enum { SIZE = 5 }; int main(void) { double a[SIZE] = { 1.,2.,3.,4.,5. }; FILE* fp = fopen("test.bin", "wb"); // 必须用二进制模式 fwrite(a, sizeof * a, SIZE, fp); // 写 double 的数组 fclose(fp); double b[SIZE]; fp = fopen("test.bin", "rb"); size_t ret_code = fread(b, sizeof * b, SIZE, fp); // 读 double 的数组 if (ret_code == SIZE) { puts("Array read successfully, contents: "); for (int n = 0; n < SIZE; ++n) printf("%f ", b[n]); putchar('\n'); } else { // error handling if (feof(fp)) printf("Error reading test.bin: unexpected end of file\n"); else if (ferror(fp)) { perror("Error reading test.bin"); } } fclose(fp); }