C语言从入门到实战——文件操作
文件操作
- 前言
- 一、 为什么使用文件
- 二、 什么是文件
- 2.1 程序文件
- 2.2 数据文件
- 2.3 文件名
- 三、 二进制文件和文本文件
- 四、 文件的打开和关闭
- 4.1 流和标准流
- 4.1.1 流
- 4.1.2 标准流
- 4.2 文件指针
- 4.3 文件的打开和关闭
- 4.4 文件的路径
- 五、 文件的顺序读写
- 5.1 顺序读写函数介绍
- fgetc
- fputc
- fgets
- fputs
- fscanf
- fprintf
- fread
- fwrite
- 5.2 对比一组函数
- sscanf
- sprintf
- 5.3标准的数据拷贝
- 六、 文件的随机读写
- 6.1 fseek
- 6.2 ftell
- 6.3 rewind
- 七、 文件读取结束的判定
- fgetc
- fgets
- 7.1 被错误使用的 feof
- 八、 文件缓冲区
前言
C语言中的文件操作是通过使用文件指针来实现的。可以使用标准库中的函数来打开、读取、写入和关闭文件。
下面是一些常见的文件操作函数:
- 打开文件:使用
fopen()
函数来打开文件,该函数接受两个参数,文件名和打开方式。打开方式可以是"r"
(只读模式),"w"
(写入模式),"a"
(追加模式)等等。函数返回一个文件指针,可以在后续的操作中使用。
FILE *fp;
fp = fopen("filename.txt", "r");
- 读取文件内容:使用
fscanf()
函数来从文件中读取内容。该函数接受一个文件指针和一个格式字符串作为参数,读取文件中的数据并按照格式字符串的指示将数据存储到相应的变量中。
int num;
fscanf(fp, "%d", &num);
- 写入文件内容:使用
fprintf()
函数来向文件中写入内容。该函数接受一个文件指针和一个格式字符串作为参数,将相应的数据按照格式字符串的指示写入文件。
int num = 10;
fprintf(fp, "%d", num);
- 关闭文件:使用
fclose()
函数来关闭文件,该函数接受一个文件指针作为参数。
fclose(fp);
在进行文件操作时,需要注意以下几点:
- 在打开文件之前要确保文件存在,或者在打开文件时使用合适的打开方式。
- 在读取和写入文件时要确保文件指针指向正确的位置,可以使用
fseek()
函数来调整文件指针的位置。 - 每次读取或写入文件后,都要检查函数的返回值,以确保文件操作成功。
- 在完成文件操作后,一定要关闭文件,释放文件指针所占用的资源。
这是一个简单的文件操作的示例:
#include <stdio.h>int main() {FILE *fp;int num;// 打开文件fp = fopen("numbers.txt", "r");if (fp == NULL) {printf("无法打开文件\n");return 1;}// 读取文件内容fscanf(fp, "%d", &num);printf("文件中的数字是:%d\n", num);// 关闭文件fclose(fp);return 0;
}
在上面的示例中,程序打开了一个名为 numbers.txt
的文件,读取文件中的一个整数,并打印出来。最后关闭了文件。
一、 为什么使用文件
如果没有文件,我们写的程序的数据是存储在电脑的内存中,如果程序退出,内存回收,数据就丢失了,等再次运行程序,是看不到上次程序的数据的,如果要将数据进行持久化的保存,我们可以使用文件。
二、 什么是文件
磁盘上的文件是文件。
但是在程序设计中,我们一般谈的文件有两种:程序文件、数据文件(从文件功能的角度来分类的)。
文件的本质是结构体
2.1 程序文件
程序文件包括源程序文件(后缀为.c
),目标文件(windows
环境后缀为.obj
),可执行程序(windows
环境后缀为.exe
)。
2.2 数据文件
文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件,或者输出内容的文件。
在以前文章中所处理数据的输入输出都是以终端为对象的,即从终端的键盘输入数据,运行结果显示到显示器上。
其实有时候我们会把信息输出到磁盘上,当需要的时候再从磁盘上把数据读取到内存中使用,这里处理的就是磁盘上文件。
2.3 文件名
一个文件要有一个唯一的文件标识,以便用户识别和引用。
文件名包含3部分:文件路径+文件名主干+文件后缀
例如: c:\code\test.txt
为了方便起见,文件标识常被称为文件名。
三、 二进制文件和文本文件
根据数据的组织形式,数据文件被称为文本文件或者二进制文件。
数据在内存中以二进制的形式存储,如果不加转换的输出到外存,就是二进制文件。
如果要求在外存上以ASCII
码的形式存储,则需要在存储前转换。以ASCII
字符的形式存储的文件就是文本文件。
一个数据在内存中是怎么存储的呢?
字符一律以ASCII
形式存储,数值型数据既可以用ASCII
形式存储,也可以使用二进制形式存储。如有整数10000
,如果以ASCII
码的形式输出到磁盘,则磁盘中占用5个字节(每个字符一个字节),而二进制形式输出,则在磁盘上只占4个字节(VS2019测试)。
#include <stdio.h>
int main()
{int a = 10000;FILE* pf = fopen("test.txt", "wb");fwrite(&a, 4, 1, pf); //二进制的形式写到文件中fclose(pf);pf = NULL;return 0;
}
在VS上打开二进制文件:
四、 文件的打开和关闭
4.1 流和标准流
4.1.1 流
我们程序的数据需要输出到各种外部设备,也需要从外部设备获取数据,不同的外部设备的输入输出操作各不相同,为了方便程序员对各种设备进行方便的操作,我们抽象出了流的概念,我们可以把流想象成流淌着字符的河。
C程序针对文件、画面、键盘等的数据输入输出操作都是通过流操作的。
一般情况下,我们要想向流里写数据,或者从流中读取数据,都是要打开流,然后操作。
4.1.2 标准流
那为什么我们从键盘输入数据,向屏幕上输出数据,并没有打开流呢?
标准输入输出流是默认打开的,可以直接使用
那是因为C语言程序在启动的时候,默认打开了3个流:
stdin
-标准输入流,在大多数的环境中从键盘输入,scanf
函数就是从标准输入流中读取数据。stdout
-标准输出流,大多数的环境中输出至显示器界面,printf
函数就是将信息输出到标准输出流中。stderr
-标准错误流,大多数环境中输出到显示器界面。
这是默认打开了这三个流,我们使用scanf
、printf
等函数就可以直接进行输入输出操作的。
stdin
、stdout
、stderr
三个流的类型是: FILE*
,通常称为文件指针。
C语言中,就是通过 FILE*
的文件指针来维护流的各种操作的。
4.2 文件指针
缓冲文件系统中,关键的概念是“文件类型指针”,简称“文件指针”。
每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名字,文件状态及文件当前的位置等)。这些信息是保存在一个结构体变量中的。该结构体类型是由系统声明的,取名FILE
.
例如,VS2013编译环境提供的 stdio.h
头文件中有以下的文件类型申明:
struct _iobuf {char *_ptr;int _cnt;char *_base;int _flag;int _file;int _charbuf;int _bufsiz;char *_tmpfname;
};
typedef struct _iobuf FILE;
不同的C编译器的FILE
类型包含的内容不完全相同,但是大同小异。
每当打开一个文件的时候,系统会根据文件的情况自动创建一个FILE
结构的变量,并填充其中的信息,使用者不必关心细节。
一般都是通过一个FILE
的指针来维护这个FILE
结构的变量,这样使用起来更加方便。
下面我们可以创建一个FILE*
的指针变量:
FILE* pf; //文件指针变量
定义pf
是一个指向FILE
类型数据的指针变量。可以使pf
指向某个文件的文件信息区(是一个结构体变量)。通过该文件信息区中的信息就能够访问该文件。也就是说,通过文件指针变量能够间接找到与它关联的文件。
比如:
创建文件变量,data
的数据会拷贝到f
这个文件结构体中,将这个文件结构体填满
4.3 文件的打开和关闭
文件操作,操作成功返回文件指针,操作失败会返回NULL
文件在读写之前应该先打开文件,在使用结束之后应该关闭文件。
在编写程序的时候,在打开文件的同时,都会返回一个FILE*
的指针变量指向该文件,也相当于建立了指针和文件的关系。
ANSIC
规定使用 fopen
函数来打开文件,fclose
来关闭文件。
//打开文件
FILE * fopen ( const char * filename, const char * mode );
//关闭文件
int fclose ( FILE * stream );
mode
表示文件的打开模式,下面都是文件的打开模式:
文件使用方式 | 含义 | 如果指定文件不存在 |
---|---|---|
“r ”(只读) | 为了输入数据,打开一个已经存在的文本文件 | 出错 |
“w ”(只写) | 为了输出数据,打开一个文本文件 | 建立一个新的文件 |
“a ”(追加) | 向文本文件尾添加数据 | 建立一个新的文件 |
“rb ”(只读) | 为了输入数据,打开一个二进制文件 | 出错 |
“wb ”(只写) | 为了输出数据,打开一个二进制文件 | 建立一个新的文件 |
“ab ”(追加) | 向一个二进制文件尾添加数据 | 建立一个新的文件 |
“r+ ”(读写) | 为了读和写,打开一个文本文件 | 出错 |
“w+ ”(读写) | 为了读和写,建议一个新的文件 | 建立一个新的文件 |
“a+ ”(读写) | 打开一个文件,在文件尾进行读写 | 建立一个新的文件 |
“rb+ ”(读写) | 为了读和写打开一个二进制文件 | 出错 |
“wb+ ”(读写) | 为了读和写,新建一个新的二进制文件 | 建立一个新的文件 |
“ab+ ”(读写) | 打开一个二进制文件,在文件尾进行读和写 | 建立一个新的文件 |
ps:
除了有关追加的,其余的像w和r的,在使用存入的时候会清空里面的数据,而不会保留,想要保留,要使用追加
/* fopen fclose example */
#include <stdio.h>
int main ()
{FILE * pFile;//打开文件pFile = fopen ("myfile.txt","w");//文件操作if (pFile!=NULL){fputs ("fopen example",pFile);//关闭文件fclose (pFile);}return 0;
}
4.4 文件的路径
C语言文件的路径指向文件在计算机中的位置。路径可以是绝对路径,也可以是相对路径。
绝对路径是从计算机文件系统的根目录开始的完整路径。例如,Windows系统中的绝对路径可能是"C:\Users\Administrator\Desktop\example.c"
,而Unix/Linux系统中的绝对路径可能是"/home/user/example.c"
。
相对路径是相对于当前工作目录的路径。当前工作目录是执行程序或脚本的位置。例如,当前工作目录是"/home/user"
,文件位于当前工作目录下的子目录"code"
中,则相对路径可能是"code/example.c"
。
在C语言中,使用文件操作函数打开、读取和写入文件时,需要提供文件的路径作为参数。使用绝对路径可以确保准确找到文件,而使用相对路径可以简化文件路径的书写。
在VS中,路径需要双斜杠//
.
表示当前路径 ..
表示上一级路径
绝对路径
相对路径
当前路径的上一级路径的上一级路径
五、 文件的顺序读写
5.1 顺序读写函数介绍
函数名 | 功能 | 适用于 |
---|---|---|
fgetc | 字符输入函数 | 所有输入流 |
fputc | 字符输出函数 | 所有输出流 |
fgets | 文本行输入函数 | 所有输入流 |
fputs | 文本行输出函数 | 所有输出流 |
fscanf | 格式化输入函数 | 所有输入流 |
fprintf | 格式化输出函数 | 所有输出流 |
fread | 二进制输入 | 文件 |
fwrite | 二进制输出 | 文件 |
所有流 分为标准输入输出流和文件输入输出流
上面说的适用于所有输入流一般指适用于标准输入流和其他输入流(如文件输入流);
所有输出流一般指适用于标准输出流和其他输出流(如文件输出流)。
fgetc
见下面
fputc
c语言的fputc
函数用于将一个字符写入到文件中。该函数的原型如下:
int fputc(int c, FILE *stream);
其中,c
表示要写入的字符,stream
表示要写入的文件指针。该函数返回写入的字符,如果出现错误,则返回EOF
。
下面是一个示例:
#include <stdio.h>int main() {FILE *fp;char c = 'A';// 打开文件fp = fopen("file.txt", "w");// 写入字符fputc(c, fp);// 关闭文件fclose(fp);return 0;
}
上述示例将字符'A'
写入到名为file.txt
的文件中。
fgets
见下面
fputs
C语言的fputs
函数用于将字符串写入到文件中。该函数的原型如下:
int fputs(const char *str, FILE *stream);
其中,str
表示要写入的字符串,stream
表示要写入的文件指针。该函数返回非负值表示成功,返回EOF
表示出现错误。
下面是一个示例:
#include <stdio.h>int main() {FILE *fp;char str[] = "Hello, World!";// 打开文件fp = fopen("file.txt", "w");// 写入字符串fputs(str, fp);// 关闭文件fclose(fp);return 0;
}
上述示例将字符串"Hello, World!"
写入到名为file.txt
的文件中。
fscanf
C语言中的 fscanf
函数用于从文件中读取数据。它的格式与 scanf
函数类似,但需要指定要读取的文件。
int fscanf(FILE *stream, const char *format, ...);
其中,stream
是指向要读取的文件的指针,format
是要读取的数据的格式字符串,...
表示可以接收任意数量和类型的参数,用来接收读取的数据。
下面是一个示例:
#include <stdio.h>int main() {FILE *file;int num1, num2;file = fopen("data.txt", "r"); // 打开文件if(file == NULL) {printf("无法打开文件\n");return 1;}fscanf(file, "%d %d", &num1, &num2); // 从文件中读取两个整数printf("从文件中读取的两个整数分别为:%d 和 %d\n", num1, num2);fclose(file); // 关闭文件return 0;
}
上述示例中,我们首先使用 fopen
函数打开名为 "data.txt"
的文件。然后,使用 fscanf
函数从文件中读取两个整数,并将它们存储到变量 num1
和 num2
中。最后,使用 printf
函数将读取的两个整数输出到屏幕上。
需要注意的是,在使用 fscanf
函数读取文件数据之前,我们需要确保文件已经成功打开,并且在使用完毕后需要使用 fclose
函数关闭文件。
fprintf
C语言中的 fprintf
函数用于将指定的数据按照指定的格式写入到文件中。它的格式与 printf
函数类似,但需要指定要写入的文件。
int fprintf(FILE *stream, const char *format, ...);
其中,stream
是指向要写入的文件的指针,format
是要写入的数据的格式字符串,...
表示可以接收任意数量和类型的参数,用来传递要写入的数据。
下面是一个示例:
#include <stdio.h>int main() {FILE *file;int num = 10;file = fopen("data.txt", "w"); // 打开文件if(file == NULL) {printf("无法打开文件\n");return 1;}fprintf(file, "The number is %d\n", num); // 将整数写入文件fclose(file); // 关闭文件return 0;
}
上述示例中,我们首先使用 fopen
函数打开名为 "data.txt"
的文件,并指定以写入方式打开文件。然后,使用 fprintf
函数将整数 num
写入文件,同时按照指定的格式进行格式化输出。最后,使用 fclose
函数关闭文件。
需要注意的是,在使用 fprintf
函数写入文件数据之前,我们需要确保文件已经成功打开,并且在使用完毕后需要使用 fclose
函数关闭文件。
fread
在 C 语言中,fread
函数用于从文件中读取指定数量的数据,并将其存储到缓冲区中。它的函数原型如下:
size_t fread(void *ptr, size_t size, size_t count, FILE *stream);
其中,ptr
是一个指向要读取数据的缓冲区的指针,size
是每个数据项的大小(以字节为单位),count
是要读取的数据项的数量, stream
是一个指向要读取的文件的指针。
fread
函数会返回实际读取的数据项数量,若返回值小于 count
,可能是因为已经到达了文件结尾或发生了读取错误。
下面是一个示例,演示如何使用 fread
从文件中读取数据:
#include <stdio.h>int main() {FILE *file;int numbers[5];file = fopen("data.txt", "r"); // 打开文件if (file == NULL) {printf("无法打开文件\n");return 1;}fread(numbers, sizeof(int), 5, file); // 从文件中读取5个整数for (int i = 0; i < 5; i++) {printf("%d ", numbers[i]);}fclose(file); // 关闭文件return 0;
}
在上述示例中,我们首先使用 fopen
函数打开名为 "data.txt"
的文件,并指定以只读方式打开文件。然后,使用 fread
函数从文件中读取 5 个整数,将其存储在 numbers
数组中。最后,使用循环输出读取到的整数。
需要注意的是,在使用 fread
函数读取文件数据之前,我们需要确保文件已经成功打开,并且在使用完毕后需要使用 fclose
函数关闭文件。
fwrite
在 C 语言中,fwrite
函数用于将数据从内存写入到文件中。它的函数原型如下:
size_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream);
其中,ptr
是指向要写入的数据的指针,size
是每个数据项的大小(以字节为单位),count
是要写入的数据项的数量,stream
是一个指向要写入的文件的指针。
fwrite
函数会返回实际写入的数据项数量,若返回值小于 count
,可能是因为写入错误或者磁盘已满。
下面是一个示例,演示如何使用 fwrite
将数据写入文件:
#include <stdio.h>int main() {FILE *file;int numbers[] = {1, 2, 3, 4, 5};file = fopen("data.txt", "w"); // 打开文件if (file == NULL) {printf("无法打开文件\n");return 1;}fwrite(numbers, sizeof(int), 5, file); // 将5个整数写入文件fclose(file); // 关闭文件return 0;
}
在上述示例中,我们首先使用 fopen
函数以写入方式打开名为 “data.txt” 的文件。然后,使用 fwrite
函数将 numbers
数组中的 5 个整数写入文件。最后,使用 fclose
函数关闭文件。
需要注意的是,在使用 fwrite
函数写入文件数据之前,我们需要确保文件已经成功打开,并在使用完毕后使用 fclose
函数关闭文件。
5.2 对比一组函数
scanf/fscanf/sscanf
printf/fprintf/sprintf
fscanf
和 fprintf
,跟printf
和 scanf
相比 只是前面多了一个文件指针
sscanf
sscanf
函数是C语言中的一个输入函数,用于从字符串中按照指定的格式解析数据。它的函数原型为:
int sscanf(const char* str, const char* format, ...);
其中,str
是要解析的字符串,format
是解析的格式字符串,…表示可变参数列表,表示要解析的数据的地址。
sscanf
函数根据format
中的格式指定符,从str
中读取数据,并将解析出的数据存放在可变参数列表中的相应位置。它可以解析各种类型的数据,比如整数、浮点数、字符等。
下面是一个使用sscanf
函数的例子:
#include <stdio.h>int main() {char str[] = "10 3.14 Hello";int a;float b;char c[10];sscanf(str, "%d %f %s", &a, &b, c);printf("a = %d, b = %f, c = %s\n", a, b, c);return 0;
}
输出结果为:
a = 10, b = 3.140000, c = Hello
可以看到,sscanf
函数按照指定的格式从字符串中解析出了整数、浮点数和字符串,并赋值给相应的变量。
sprintf
sprintf
函数是C语言中的一个输出函数,用于将格式化的数据写入字符串中。它的函数原型为:
int sprintf(char* str, const char* format, ...);
其中,str
是要写入的字符串,format
是格式化字符串,...
表示可变参数列表,表示要写入的数据。
sprintf
函数根据format
中的格式指定符,将可变参数列表中的数据按照指定格式写入到str
中。它可以写入各种类型的数据,比如整数、浮点数、字符串等。
下面是一个使用sprintf
函数的例子:
#include <stdio.h>int main() {char str[50];int a = 10;float b = 3.14;char c[] = "Hello";sprintf(str, "a = %d, b = %f, c = %s", a, b, c);printf("str = %s\n", str);return 0;
}
输出结果为:
str = a = 10, b = 3.140000, c = Hello
可以看到,sprintf
函数根据指定的格式将整数、浮点数和字符串转换为字符串,并写入到str
中。最终str
中包含了格式化后的数据。
fscanf
必须和 fprintf
的格式相同,不然会出现读入错误的问题
5.3标准的数据拷贝
六、 文件的随机读写
在文件中,每读取一个字符,文件中的光标都会向后移动一位
6.1 fseek
根据文件指针的位置和偏移量来定位文件指针。
int fseek ( FILE * stream, long int offset, int origin );
设置的指针的位置是 起始位置 + 偏移量 ;
其中的 int origin
参数就是 起始位置 , 有以下三种选择 :
文件头 SEEK_SET
0
当前位置 SEEK_CUR
1
文件尾 SEEK_END
2
long offset
偏移量参数 , 可以为正数 , 也可以为负数 ;
如果执行成功 , 则返回 0
, 失败返回非 0
, 并设置 error
错误代码 ;
例子:
/* fseek example */
#include <stdio.h>
int main ()
{FILE * pFile;pFile = fopen ( "example.txt" , "wb" );fputs ( "This is an apple." , pFile );fseek ( pFile , 9 , SEEK_SET );fputs ( " sam" , pFile );fclose ( pFile );return 0;
}
6.2 ftell
返回文件指针相对于起始位置的偏移量
long int ftell ( FILE * stream );
例子:
/* ftell example : getting size of a file */
#include <stdio.h>
int main ()
{FILE * pFile;long size;pFile = fopen ("myfile.txt","rb");if (pFile==NULL)perror ("Error opening file");else{fseek (pFile, 0, SEEK_END); // non-portablesize=ftell (pFile);fclose (pFile);printf ("Size of myfile.txt: %ld bytes.\n",size);}return 0;
}
6.3 rewind
让文件指针的位置回到文件的起始位置
void rewind ( FILE * stream );
例子:
/* rewind example */
#include <stdio.h>
int main ()
{int n;FILE * pFile;char buffer [27];pFile = fopen ("myfile.txt","w+");for ( n='A' ; n<='Z' ; n++)fputc ( n, pFile);rewind (pFile);fread (buffer,1,26,pFile);fclose (pFile);buffer[26]='\0';printf(buffer);return 0;
}
七、 文件读取结束的判定
fgetc
在C语言中,fgetc
函数用于从文件中读取一个字符。它接受一个文件指针作为参数,并返回文件中的下一个字符(以整数形式)。如果到达文件末尾或发生错误,它返回特殊值EOF(-1)
。以下是一个使用示例:
#include <stdio.h>int main() {FILE *file = fopen("example.txt", "r"); // 以只读方式打开文件int c;if (file == NULL) {printf("无法打开文件。");return 1;}while ((c = fgetc(file)) != EOF) {printf("%c", c);}fclose(file); // 关闭文件return 0;
}
在这个示例中,程序打开了一个名为"example.txt"
的文件,并逐个字符读取其内容,直到达到文件末尾。然后,将每个字符打印到控制台上。最后,关闭文件以释放系统资源。
fgets
在C语言中,fgets
函数用于从文件中读取一行文本。它接受三个参数:一个字符数组指针,用于存储读取的字符串;一个整数值,表示要读取的最大字符数(包括空字符);一个文件指针,指定要从中读取的文件。
fgets
函数会一直读取字符,直到遇到换行符(包括换行符在内)或达到指定的最大字符数。读取的字符串将存储在指定的字符数组中,并在结束时自动添加一个空字符。
以下是一个使用示例:
#include <stdio.h>int main() {FILE *file = fopen("example.txt", "r"); // 以只读方式打开文件char buffer[100];if (file == NULL) {printf("无法打开文件。");return 1;}while (fgets(buffer, sizeof(buffer), file) != NULL) {printf("%s", buffer);}fclose(file); // 关闭文件return 0;
}
在这个示例中,程序打开了一个名为"example.txt"
的文件,并逐行读取其内容。读取的每一行被存储在名为buffer
的字符数组中,并随后被打印到控制台上。最后,关闭文件以释放系统资源。
7.1 被错误使用的 feof
牢记:在文件读取过程中,不能用feof
函数的返回值直接来判断文件的是否结束。
feof
的作用是:当文件读取结束的时候,判断是读取结束的原因是否是:遇到文件尾结束。
- 文本文件读取是否结束,判断返回值是否为
EOF ( fgetc )
,或者NULL ( fgets )
例如:
fgetc
判断是否为EOF
.fgets
判断返回值是否为NULL
.
- 二进制文件的读取结束判断,判断返回值是否小于实际要读的个数。
例如:
fread
判断返回值是否小于实际要读的个数。
文本文件的例子:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{int c; // 注意:int,非char,要求处理EOFFILE* fp = fopen("test.txt", "r");if(!fp) {perror("File opening failed");return EXIT_FAILURE;}//fgetc 当读取失败的时候或者遇到文件结束的时候,都会返回EOFwhile ((c = fgetc(fp)) != EOF) // 标准C I/O读取文件循环{putchar(c);}//判断是什么原因结束的if (ferror(fp))puts("I/O error when reading");else if (feof(fp))puts("End of file reached successfully");fclose(fp);
}
二进制文件的例子:
#include <stdio.h>
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 handlingif (feof(fp))printf("Error reading test.bin: unexpected end of file\n");else if (ferror(fp)) {perror("Error reading test.bin");}}fclose(fp);
}
八、 文件缓冲区
ANSIC
标准采用“缓冲文件系统”处理的数据文件的,所谓缓冲文件系统是指系统自动地在内存中为程序中每一个正在使用的文件开辟一块“文件缓冲区”。从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘上。如果从磁盘向计算机读入数据,则从磁盘文件中读取数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。缓冲区的大小根据C编译系统决定的。
fflush
刷新缓存区 写入的数据放入缓冲区,放满了才能写,或者刷新缓冲区也能写
#include <stdio.h>
#include <windows.h>
//VS2019 WIN11环境测试
int main()
{FILE*pf = fopen("test.txt", "w");fputs("abcdef", pf); //先将代码放在输出缓冲区printf("睡眠10秒-已经写数据了,打开test.txt文件,发现文件没有内容\n");Sleep(10000);printf("刷新缓冲区\n");fflush(pf); //刷新缓冲区时,才将输出缓冲区的数据写到文件(磁盘)//注:fflush 在高版本的VS上不能使用了printf("再睡眠10秒-此时,再次打开test.txt文件,文件有内容了\n");Sleep(10000);fclose(pf);//注:fclose在关闭文件的时候,也会刷新缓冲区pf = NULL;return 0;
}
这里可以得出一个结论:
因为有缓冲区的存在,C语言在操作文件的时候,需要做刷新缓冲区或者在文件操作结束的时候关闭文件。
如果不做,可能导致读写文件的问题。
相关文章:

C语言从入门到实战——文件操作
文件操作 前言一、 为什么使用文件二、 什么是文件2.1 程序文件2.2 数据文件2.3 文件名 三、 二进制文件和文本文件四、 文件的打开和关闭4.1 流和标准流4.1.1 流4.1.2 标准流 4.2 文件指针4.3 文件的打开和关闭4.4 文件的路径 五、 文件的顺序读写5.1 顺序读写函数介绍fgetcfp…...

数据结构中的一棵树
一、树是什么? 有根有枝叶便是树!根只有一个,枝叶可以有,也可以没有,可以有一个,也可以有很多。 就像这样: 嗯,应该是这样: 二、一些概念 1、高度 树有多高&#x…...

C++中的static(静态)
2014年1月19日 内容整理自The Cherno:C系列 2014年1月20日 内容整理自《程序设计教程:用C语言编程 第三版》 陈家骏 郑滔 -----------------------------------------------------------------------------------------------------------------------------…...

常见框架漏洞
1.什么是框架 Web框架(Web framework)或者叫做Web应用框架(Web application framework),是用于进行Web开发的一套软件架构。大多数的Web框架提供了一套开发和部署网站的方式。为Web的行为提供了一套支持的方法。使用Web框架,很多的业务逻辑外的功能不需…...

Python文件自动化处理
os模块 Python标准库和操作系统有关的操作创建、移动、复制文件和文件夹文件路径和名称处理 路径的操作 获取当前Python程序运行路径不同操作系统之间路径的表示方式 windows中采用反斜杠(\)作为文件夹之间的分隔符 Mac和Linux中采用斜杠(/)作为文件夹之间的分隔符 把文件…...
js变量提升
js变量提升 在JavaScript中,变量提升(Hoisting)是一种特殊的语法行为,它允许变量和函数声明在它们实际出现之前被JavaScript引擎识别。这意味着,当你在代码的后面部分使用一个变量或函数时,JavaScript引擎…...

C++ 设计模式之策略模式
【声明】本题目来源于卡码网(题目页面 (kamacoder.com)) 【提示:如果不想看文字介绍,可以直接跳转到C编码部分】 【设计模式大纲】 【简介】什么是策略模式(第14种模式) 策略模式是⼀种⾏为型设计模式&…...
(202401)深度强化学习基础2:策略梯度
文章目录 前言策略梯度1 基于价值算法的缺点2 策略梯度算法3 REINFORCE算法本章小结 前言 感谢Datawhale成员的开源本次学习内容的文档地址为 第九章 策略梯度 策略梯度 这个章节会开始介绍基于策略梯度的算法。前面的算法都是针对“奖励”或者说“回报(reward&a…...

bgp大AS小AS选路-联邦ebgp选路
效果图:R1 ping 通 R8 环回 R4的bgp路由表中5.5.5.5通过修改起源属性,下一跳R7变为R2, 即原本走下面R4-R7-R6-R5,改成R4-R3-R2-R5 R5效果图和R4类似(不放了),R5的bgp路由表中4.4.4.4下一跳从R2优先改为R7优先(即原本走上面路R4-R3-R2-R5,改成下面路R4-R7-R6-R5),通…...

beego API 自动化文档
API 全局设置 必须设置在 routers/router.go 中,文件的注释,最顶部: // APIVersion 1.0.0 // Title mobile API // Description mobile has every tool to get any job done, so codename for the new mobile APIs. // Contact astaxiegmai…...

百度搜索Push个性化:新的突破
作者 | 通用搜索产品研发组 导读 本文简单介绍了百度搜索Push个性化的发展过程,揭示了面临的困境和挑战:如何筛选优质物料、如何对用户精准推荐等。我们实施了一系列策略方法进行突破,提出核心的解决思路和切实可行的落地方案。提升了搜索DAU…...
【Oracle】ORA-32017和ORA-00384错误处理
文章目录 【Oracle】ORA-32017和ORA-00384错误处理问题描述问题原因和解决测试验证 【声明】文章仅供学习交流,观点代表个人,与任何公司无关。 编辑|SQL和数据库技术(ID:SQLplusDB) 收集Oracle数据库内存相关的信息 【Oracle】ORA-32017和ORA-00384错误…...

MySQL三大日志
1. redo log 1.1 特点 InnoDB存储引擎独有物理日志,记录在数据页上做的修改让MySQL拥有了崩溃恢复能力,保证事务的持久性 1.2 刷盘时机 事务提交时log buffer 空间使用大约一半时事务日志缓冲区满InnoDB 定期执行检查点Checkpoint后台刷新线程&#…...

力扣每日一练(24-1-20)
大脑里的第一想法是排列组合,直接给出超级准确的最优解。 但不适用,hhh 只要连续的n个元素大于或者等于target就可以了 题目比自己想象的要好解决 解法是使用滑动窗口算法。这个算法的基本思想是维护一个窗口,使得窗口内的元素总和大于等于目…...

Pytest系列(2) - assert断言详细使用
前言 与unittest不同,pytest使用的是python自带的assert关键字来进行断言assert关键字后面可以接一个表达式,只要表达式的最终结果为True,那么断言通过,用例执行成功,否则用例执行失败 assert小栗子 想在抛出异常之…...

CodeWave智能开发平台--03--目标:应用创建--10初级采购管理系统总结
摘要 本文是网易数帆CodeWave智能开发平台系列的第14篇,主要介绍了基于CodeWave平台文档的新手入门进行学习,实现一个完整的应用,本文主要完成10初级采购管理系统总结 CodeWave智能开发平台的14次接触 CodeWave参考资源 网易数帆CodeWave…...

外包干了4个月,技术退步明显.......
先说一下自己的情况,大专生,18年通过校招进入武汉某软件公司,干了接近4年的功能测试,今年年初,感觉自己不能够在这样下去了,长时间呆在一个舒适的环境会让一个人堕落! 而我已经在一个企业干了四年的功能测…...

图片批量建码怎么用?每张图片快速生成二维码
当我们需要给每个人分别下发对应的个人证件类图片信息,比如制作工牌、荣誉展示或者负责人信息展示时,现在都开始使用二维码的方法来展示员工信息。那么如何快速将每个人员的信息图片分别制作成二维码图片呢,最简单的方法就是使用图片批量建码…...
时间复杂度的排序
在计算机科学中,不同的算法有不同的时间复杂度。以下是一些常见的时间复杂度,并按照它们的增长速度从低到高排序: O(1) - 常数时间复杂度: 表示算法的执行时间是固定的,不随输入规模的增加而变化。例如,直接…...
js控制浏览器前进、后退、页面跳转
在JavaScript中,你可以使用 window 对象的 history 对象来控制浏览器的历史记录。以下是一些常用的方法: 前进和后退: window.history.forward(): 前进到历史记录中的下一个页面。window.history.back(): 返回历史记录中的上一个页面。window…...
【解密LSTM、GRU如何解决传统RNN梯度消失问题】
解密LSTM与GRU:如何让RNN变得更聪明? 在深度学习的世界里,循环神经网络(RNN)以其卓越的序列数据处理能力广泛应用于自然语言处理、时间序列预测等领域。然而,传统RNN存在的一个严重问题——梯度消失&#…...

全球首个30米分辨率湿地数据集(2000—2022)
数据简介 今天我们分享的数据是全球30米分辨率湿地数据集,包含8种湿地亚类,该数据以0.5X0.5的瓦片存储,我们整理了所有属于中国的瓦片名称与其对应省份,方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...
【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表
1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...
【论文笔记】若干矿井粉尘检测算法概述
总的来说,传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度,通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...
根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:
根据万维钢精英日课6的内容,使用AI(2025)可以参考以下方法: 四个洞见 模型已经比人聪明:以ChatGPT o3为代表的AI非常强大,能运用高级理论解释道理、引用最新学术论文,生成对顶尖科学家都有用的…...

3-11单元格区域边界定位(End属性)学习笔记
返回一个Range 对象,只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意:它移动的位置必须是相连的有内容的单元格…...
.Net Framework 4/C# 关键字(非常用,持续更新...)
一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...
重启Eureka集群中的节点,对已经注册的服务有什么影响
先看答案,如果正确地操作,重启Eureka集群中的节点,对已经注册的服务影响非常小,甚至可以做到无感知。 但如果操作不当,可能会引发短暂的服务发现问题。 下面我们从Eureka的核心工作原理来详细分析这个问题。 Eureka的…...
CSS设置元素的宽度根据其内容自动调整
width: fit-content 是 CSS 中的一个属性值,用于设置元素的宽度根据其内容自动调整,确保宽度刚好容纳内容而不会超出。 效果对比 默认情况(width: auto): 块级元素(如 <div>)会占满父容器…...

技术栈RabbitMq的介绍和使用
目录 1. 什么是消息队列?2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...