C文件读写

您所在的位置:网站首页 fwrite二进制 C文件读写

C文件读写

2023-03-09 04:32| 来源: 网络整理| 查看: 265

可以将程序中的数据保存为一个文件。待下次重新启动程序时,从之前保存的文件中提取数据。这样,程序就不会在重启后失忆了。

创建并写入文件 #include int main() { // 创建一个名为data.txt的文件 FILE* pFile = fopen("data.txt", "w"); if (pFile == NULL) { // 文件创建失败 return -1; } // 文件创建成功 int n = 123; double f = 3.1415; char ch = 'A'; // fprintf第一个参数为文件结构指针,其后参数与printf一致 fprintf(pFile, "%d\n", n); fprintf(pFile, "%f\n", f); fprintf(pFile, "%c\n", ch); // 关闭文件 fclose(pFile); return 0; }

打开文件data.txt,我们可以发现,里面有刚刚写入的三个变量的值,并且每打印一个变量换行一次。

123 3.141500 A

为了操作文件,我们需要借助几个在头文件stdio.h中声明的库函数。

创建或打开文件fopen函数。

FILE *fopen (const char * filename, const char * mode); 输入: const char * filename文件路径,可以使用相对路径或绝对路径。 const char * mode操作模式 输出: 如果文件创建或打开成功,则返回一个指针。这个指针指向一个记录文件信息的结构FILE。其他各种文件操作函数,需要这个结构指针才能对fopen打开或创建的文件进行操作。我们无需过多地关注这个结构的具体组成,仅需要将这个结构指针传递给各种文件操作函数即可。 例如,我们使用相对路径data.txt,将在当前目录下,创建一个名为data.txt的文件。 也可以在windows上使用形如F:/projects/data.txt的绝对路径,在F盘下的project文件夹中,创建data.txt文件。 函数 fopen 的第一个参数为字符串,内容为需要操作的文件路径,第二个参数也为字符串,内容为文件的操作模式。

操作模式 读、写模式w、r "r" 模式,读模式,取自read的首字母。对文件进行读取操作。 "w" 模式,写模式,取自write的首字母。对文件进行写入操作。如果文件存在,清空原文件内容,不存在则创建一个新文件。 追加模式a

如果,现在想在第一行后,再增加更多的HelloWorld,若函数fopen使用的是w写入模式,文件将清空原内容再写入。现在,我们需要保留原有内容,继续在文件尾部添加新内容。这时候,需要使用追加模式a。字符a为单词追加append的首字母。

#include int main() { FILE* pFile = fopen("data.txt", "a"); // 追加模式 if (pFile == NULL) { return -1; } char str[] = "HelloWorld\n"; char* p = str; while (*p != '\0') { fputc(*p, pFile); p++; } fclose(pFile); return 0; }

多运行几次,可以发现,文件中有了多行HelloWorld了。 注意,代码从未将\0写入过文件,文件中的每一行都是由换行分隔。且\0也不标记文件结尾。文件是否结尾可以通过文件操作函数返回值和feof函数的返回值判断。

可读可写模式

可以使用+将r和w模式从单一的模式,升级为读写均可模式。

"w+" 模式,更新模式,可读可写。但是,会清空文件原有内容。 "r+" 模式,更新模式,可读可写。

对于以更新模式 + 打开的文件,这里有一个必须要注意的地方:

文件从写操作转换为读操作前,必须使用fflush,fseek,rewind其中一个函数。 文件从读操作转换为写操作前,必须使用fseek,rewind其中一个函数。 字符串输出到文件内fprintf

int fprintf (FILE * stream, const char * format, ...); 若需要将字符串输出到文件内,有一个非常类似于printf的函数fprintf。它就相当于在函数printf第一个参数前,加了一个文件结构指针参数,用于指明操作哪个文件。其他的使用方法和printf几乎一致。

字符输出到文件内fputc

fputc()函数用于向文件中写入一个字符。 fputc 的函数原型: int fputc(int character, FILE* stream); 输入: int character写入文件的字符 FILE* stream文件结构指针 输出 : 如果写入成功,返回刚刚写入的字符。如果文件结尾或失败,则返回EOF。并且ferror可以检测到文件读写出错。 使用指针p的移动遍历"HelloWorld\n"字符串,直到指针指向字符为\0为止。遍历结束前的字符,均被fputc函数写入到文件当中。 请注意,目前函数fopen使用的是w写入模式。因此,文件将清空原内容再写入。

#include int main() { FILE* pFile = fopen("data.txt", "w"); // 写模式 if (pFile == NULL) { return -1; } char str[] = "HelloWorld\n"; char* p = str; while (*p != '\0') { // 向文件中写入一个字符 fputc(*p, pFile); p++; } fclose(pFile); return 0; }

程序运行完成后,将会在文件中看到一串字符HelloWorld并换行。

关闭文件fclose(pFile);

虽然程序结束会为我们自动关闭文件。如果在程序运行期间,不需要再次操作文件了,可以调用函数fclose关闭文件。并且,关闭所有资源再结束程序是一个良好的编程习惯。

文本模式与二进制模式

使用十六进制查看器,打开这个文件 很显然,这个文件里面记录了刚刚写入字符的ASCII码。 十六进制0A,换行符,转义序列为'\n'。 十六进制0D,回车,转义序列为'\r'。

为什么会出现回车和换行两个字符

在早期的电传打字机上,有一个部件叫“字车”,类似于打印机的喷头。“字车”从最左端开始,每打一个字符,“字车”就向右移动一格。当打满一行字后,“字车”需要回到最左端。这个动作被称作“回车”(return carriage)。 但是,仅仅做了“回车”还不够,我们还需要将纸张上移一行,让“字车”对准新的空白一行。否则,两行字将被重叠打印在一起。这个动作被称作“换行”。 随着时代的发展,字符不仅仅只打印在纸上。例如,在屏幕上打印字符时,无需“字车”。 所以,当人们将开始新的一行引入到计算机上时,分成了两种惯例:

沿用这两个动作,回车加换行\r、\n。 简化为仅换行\n。

两类具有代表性的系统分别使用了其中一种惯例:

Windows系统使用\r加\n。 Linux系统使用\n。

C语言本身采取了第二种惯例,仅使用一个字符\n。但是,为了适配各系统下的惯例,C语言写入、读取文件时,若系统惯例与C语言使用的不一致,则会自动进行转换。 Linux系统和C语言采用同一种惯例\n,无需转换。 C语言在Windows系统上写入文件时,会将\n写入为\r、\n。而读取文件时,会将\r、\n读取为\n。 如果在windows系统上运行刚刚的代码,文件内换行将是\r、\n两个字符。 如果在linux系统上运行刚刚的代码,文件内换行将是\n一个字符。

正是因为C语言把对文件输入输出的数据当做一行行的文本来处理,才会有这种换行时的自动转换的现象。这种文件操作模式被称作文本模式。

二进制模式

如果,不希望C语言把对文件输入输出的数据当做文本,不进行换行时的自动转换。可以在打开文件时使用二进制模式。在函数fopen的第二个参数的字符串中添加字符b,代表二进制binary。 FILE *pFile = fopen("data.txt", "wb"); // 二进制写模式 FILE *pFile = fopen("data.txt", "rb"); // 二进制读模式

读取文件 fscanf函数

fscanf相当于在函数scanf第一个参数前,加了一个文件结构指针参数,用于指明操作哪个文件。其他的使用方法和scanf几乎一致。

fscanf的函数原型:

int fscanf(FILE* stream, const char* format, ...); 现在需要从文件中读取数据,所以使用只读r模式打开文件。

#include int main() { // 读取一个名为data.txt的文件 FILE* pFile = fopen("data.txt", "r"); if (pFile == NULL) { // 文件打开失败 return -1; } // 文件打开成功 int n; double f; char ch; // fscanf第一个参数为文件结构指针,其后参数与fscanf一致 fscanf(pFile, "%d", &n); fscanf(pFile, "%lf", &f); fscanf(pFile, "%c", &ch); printf("%d\n", n); printf("%f\n", f); printf("%c\n", ch); // 关闭文件 fclose(pFile); return 0; }

函数fscanf成功地从文件中读取出了前两个数据,第三个数据读取失败了。这是因为第三个fscanf的%c占位符期望获取一个字符。而上一行末尾中,刚好有一个\n。因此,第三个fscanf读取了\n并赋值给了变量ch。 可以使用类似于getchar()函数的fgetc,从文件中读取一个字符,吸收这个\n。

fgetc函数

int fgetc(FILE* stream); 输入: FILE * stream文件结构指针 输出: 如果读取成功,返回读取到的字符。如果文件结尾或失败,则返回EOF。

#include int main() { FILE* pFile = fopen("data.txt", "r"); if (pFile == NULL) { return -1; } int n; double f; char ch; fscanf(pFile, "%d", &n); fscanf(pFile, "%lf", &f); // 吸收上一行末尾的'\n' fgetc(pFile); fscanf(pFile, "%c", &ch); printf("%d\n", n); printf("%f\n", f); printf("%c\n", ch); fclose(pFile); return 0; } fgets函数

char* fgets(char* str, int num, FILE* stream); 输入:

str将读取的一行字符串存储在 str 为首地址的空间中。 num最大的读取字符数,包括 '\n' 在内。 stream文件结构指针

例如,我们先声明100个字节的 char 类型的数组,数组名为 str ,用于放置从文件中读取的一行字符串。若文件中有一行超过100个字符,将这一行字符串放置到str数组中,将导致越界。因此,我们可以使用第二个参数num来限制最大读取的字符数。第三个参数则是文件结构指针。

char buffer[100]; fgets(buffer, 100, pFile);

输出:

如果读取成功,函数返回str。 如果遇到文件结尾,已读取到部分数据,那么返回str。 如果遇到文件结尾,未读取到任何数据,那么返回NULL。 如果遇到文件读取错误,返回NULL。str中有可能有部分已读取数据。

根据返回值规则,若读取一行字符成功将返回str,即可再次读取下一行字符。若返回NULL,则结束读取。 在运行程序前,别忘记刚刚文件已经被清空了。先向文件写入些内容再运行程序。

#include int main() { FILE* pFile = fopen("data.txt", "r"); if (pFile == NULL) { return -1; } char buffer[100]; while (fgets(buffer, 100, pFile) != NULL) { printf("%s", buffer); } fclose(pFile); return 0; }

正常输出

123 3.141500 A EOF

EOF,是文件结尾,End Of File的首字符缩写。为头文件stdio.h中定义的一个宏,通常定义为: #define EOF (-1) 它被用于头文件stdio.h中一些函数的返回值,用于指示文件结尾或者是一些其他错误。

#include int main() { FILE* pFile = fopen("data.txt", "r"); if (pFile == NULL) { return -1; } char ch; while (1) { ch = fgetc(pFile); if (ch == EOF) { // 文件结尾或者是一些其他错误 break; } putchar(ch); } fclose(pFile); return 0; } 文件状态判断

假设文件data.txt内容为

123 3.141500 A

feof用于测试是否文件结尾。 ferror用于测试文件是否读写出错。

feof函数原型

int feof(FILE* stream); 输入: FILE * stream文件结构指针 输出: 如果文件结尾,返回值为非0。否则,返回值为0。

ferror函数原型

int ferror(FILE* stream); 输入: FILE * stream文件结构指针 输出: 如果文件读写出错,返回值为非0。否则,返回值为0。 我们可以在fgetc函数返回EOF后,再次根据上述两个函数,判断究竟是文件结尾了,还是遇到了错误。

#include int main() { FILE* pFile = fopen("data.txt", "r"); if (pFile == NULL) { return -1; } char ch; while (1) { ch = fgetc(pFile); if (ch == EOF) { // 文件结尾或者是一些其他错误 if (feof(pFile) != 0) // 测试文件是否结尾 { printf("end of file\n"); } else if (ferror(pFile) != 0) // 测试文件是否读写出错 { printf("file access error\n"); } break; } putchar(ch); } fclose(pFile); return 0; }

正常结尾

123 3.141500 A end of file

如果把文件打开模式换成w写模式。那么,文件将无法被读取,尝试读取文件将产生读写错误。并且,由于**w**写模式会将已有文件清空,所以现在文件内容为空。

// 改为"w"写模式 FILE* pFile = fopen("data.txt", "w");

读写错误

file access error 文件缓存 fputs函数

fputs()函数用于向文件中写入一串字符串。 int fputs(const char* str, FILE* stream); 输入: const char* str待写入文件的字符串 FILE* stream文件结构指针 输出 : 如果写入成功,返回一个非负值。如果写入失败,则返回EOF。并且,ferror可以检测到文件读写出错。

由于用fopen函数打开文件时,使用了w写模式。因此,文件原内容将清空,写入5行Have a good time\n。

#include #include int main() { FILE* pFile = fopen("data.txt", "w"); // 写模式 if (pFile == NULL) { return -1; } char str[] = "Have a good time\n"; for (int i = 0; i < 5; i++) { fputs(str, pFile); } // 关闭文件前,先暂停一下 system("pause"); fclose(pFile); return 0; }

虽然在运行到暂停时,向文件中写入数据的fputs(str, pFile)语句已经运行过了。但是,现在打开文件,文件内没有任何内容。 让暂停继续。程序结束后,文件内出现了内容。

fflush函数

C语言中提供的文件操作函数是带有缓存的,数据会先写入到缓存中。待缓存中的数据积累到一定数量时,再一起写入文件。因此,刚刚暂停时,数据还在缓存区内,未写入到文件当中。 只有将缓存区的数据写入文件,数据才真正保存在了文件中。此时缓存区的数据无需保留将被清空。这个动作被称之为刷新缓存。 而文件关闭fclose或程序结束会刷新缓存。所以,关闭文件fclose后,文件内出现了内容。 除此之外,还可以主动调用fflush函数,主动刷新文件缓存。 int fflush(FILE* stream); 输入: FILE * stream文件结构指针 输出 刷新缓存区成功返回0,否则返回EOF,并且ferror可以检测到文件读写出错。 现在,稍微改一点代码。在程序暂停前刷新缓存区。

#include #include int main() { FILE* pFile = fopen("data.txt", "w"); // 写模式 if (pFile == NULL) { return -1; } char str[] = "Have a good time\n"; for (int i = 0; i < 5; i++) { fputs(str, pFile); } // 刷新文件缓存区后暂停程序 fflush(pFile); system("pause"); fclose(pFile); return 0; }

现在,即使未运行到fclose及程序关闭,文件中也已经有内容了。

Have a good time Have a good time Have a good time Have a good time Have a good time 文件偏移

假设现在文件data.txt内容为

Have a good time Have a good time Have a good time Have a good time Have a good time #include void fileEofOrError(FILE* pFile) { if (feof(pFile) != 0) // 测试文件是否结尾 { printf("end of file\n"); } else if (ferror(pFile) != 0) // 测试文件是否读写出错 { printf("file access error\n"); } } int main() { FILE* pFile = fopen("data.txt", "r"); if (pFile == NULL) { return -1; } char ch; while (1) { ch = fgetc(pFile); if (ch == EOF) { fileEofOrError(pFile); break; } putchar(ch); } fclose(pFile); return 0; }

输出结果

Have a good time Have a good time Have a good time Have a good time Have a good time end of file

为什么每一次的 fgetc 函数能顺序获取到文件中的字符呢?

文件指针

文件结构pFile中,保存了一个当前文件读写位置的指针。文件由fopen函数打开后,这个指针指向文件中第一个字节。当任意文件操作函数读写相应长度的字节后,指针也会偏移相应的长度。 fgetc函数每次获取一个字节。因此,文件指针向后移动一个字节。所以,重复调用fgetc函数可以逐个读取文件内的字符。 fgets函数每次获取一行字符。因此,文件指针向后移动到下一行开始。所以,重复调用fgets函数可以逐行读取文件内的字符。

文件指针移动函数fseek

int fseek(FILE* stream, long offset, int origin); 输入: FILE* stream文件结构指针 long offset文件指针偏移量 origin从什么位置开始偏移。 其中origin可以使用以下3种宏定义作为参数:

SEEK_SET文件开头(文件第一个字节) SEEK_CUR当前文件位置 SEEK_END文件结尾(文件最后一个字节后)

输出 : 如果成功,返回0。否则,则返回一个非零值。并且,ferror可以检测到文件读写出错。

从文件开头偏移5个字节,文件指针将指向a。

fseek(pFile, 5, SEEK_SET);

从文件结尾偏移-5个字节,文件指针将指向i。

fseek(pFile, -5, SEEK_END);

ftell函数获取当前文件指针位置

ftell 的函数原型: long ftell (FILE * stream); 输入: FILE * stream文件结构指针 输出: 如果成功,则返回当前文件指针位置。如果失败,则返回-1。

获取文件大小

如果将文件指针先偏移到末尾,再获取文件指针当前的位置,就能知道该文件内有多少个字节。即该文件的大小。

#include int main() { FILE* pFile = fopen("data.txt", "r"); if (pFile == NULL) { return -1; } char ch; // 偏移到文件结尾 fseek(pFile, 0, SEEK_END); // 获取当前文件指针位置 long length = ftell(pFile); printf("size of file %ld\n", length); fclose(pFile); return 0; }

输出文件大小

函数rewind,将文件指针回到文件最开始。

如果想让文件指针回到最开始,从文件开头偏移0个字节。 fseek(pFile, 0, SEEK_SET); 也可以使用函数rewind,将文件指针回到文件最开始。 rewind的函数原型: void rewind(FILE * stream); 输入: FILE * stream文件结构指针 输出: 无

更新文件

假设现在data.txt文件内容为:

Hello world Hello world Hello world Hello world Hello world

现在要将H全部改为h 为了满足需求,我们选用保留原文件内容的r+更新模式。 代码中使用fgetc读取文件中的每个字符,若读到字符H,则把这个字符使用fputc修改为h。fgetc读取到字符H后,文件指针已经指向了下一个字符。所以,若读取到字符H,需要将文件指针向前移动一个字节,再进行修改。

对于以更新模式+开的文件,这里有一个必须要注意的地方:

文件从写操作转换为读操作前,必须使用fflush,fseek,rewind其中一个函数。 文件从读操作转换为写操作前,必须使用fseek,rewind其中一个函数。

在代码中读写操作转换的地方加入必要函数。如果仅需要读写操作转换,但无需变动文件指针。可以在当前位置处偏移0字节。 fseek(pFile, 0, SEEK_CUR);

#include void fileEofOrError(FILE* pFile) { if (feof(pFile) != 0) // 测试文件是否结尾 { printf("end of file\n"); } else if (ferror(pFile) != 0) // 测试文件是否读写出错 { printf("file access error\n"); } } int main() { FILE* pFile = fopen("data.txt", "r+"); if (pFile == NULL) { return -1; } char ch; while (1) { ch = fgetc(pFile); if (ch == EOF) { fileEofOrError(pFile); break; } if (ch == 'H') { // 读转写 fseek(pFile, -1, SEEK_CUR); ch = fputc('h', pFile); if (ch == EOF) { fileEofOrError(pFile); break; } // 写转读 fflush(pFile); } } fclose(pFile); return 0; }

读转写时已经调用过fseek函数了。写转读时,可以使用fflush或fseek偏移0字节。 运行后,文件中的字符H已修改为小写的h。

读写字符串 将数值转为字符串保存 #include int main() { // 创建一个名为data.txt的文件 FILE* pFile = fopen("data.txt", "w"); if (pFile == NULL) { // 文件创建失败 return -1; } // 装有数值的数组 int numbers[8] = { 1, 12, 123, 1234, 12345, 10, 123456, 1234567 }; for (int i = 0; i < 8; i++) { // 将数值打印至文件,每行一个数值 fprintf(pFile, "%d\n", numbers[i]); } // 关闭文件 fclose(pFile); return 0; }

编译并运行后,使用文本编译器打开文件data.txt可以发现,数值已经被转为换行分隔的字符串并保存在文件中了。 若数值的十进制位数越多,字符串的字符也就越多,需要占用的空间也越大。 例如: "1" 有1个十进制位,需要1个字节。 "12345" 有5个十进制位,需要5个字节。 "1234567" 有7个十进制位,需要7个字节。

读取字符串转为数值 #include void fileEofOrError(FILE* pFile) { if (feof(pFile) != 0) // 测试文件是否结尾 { printf("end of file\n"); } else if (ferror(pFile) != 0) // 测试文件是否读写出错 { printf("file access error\n"); } } int main() { // 创建一个名为data.txt的文件 FILE* pFile = fopen("data.txt", "r"); if (pFile == NULL) { // 文件创建失败 return -1; } int numbers[8] = { 0 }; int count = 0; while (1) { // 如果数组已经填满8个元素,则不继续读取 if (count >= 8) { printf("numbers is full\n"); break; } int get = fscanf(pFile, "%d", &numbers[count]); printf("%d,", get); if (get == EOF) { fileEofOrError(pFile); break; } count++; } putchar('\n'); // 打印数组中的数值 for (int i = 0; i < 8; i++) printf("%d\n", numbers[i]); // 关闭文件 fclose(pFile); return 0; } 判断是否读完

除了使用固定长度的循环,还可以通过函数fscanf的返回值判断是否已经读完文件。 函数fscanf的返回值的意义为:参数列表中成功填充的参数个数。若文件读取失败或文件结尾,将返回EOF。 若返回EOF,此时可以通过feof以及ferror函数查询具体的原因。

防止数组越界

若文件中的字符串小于8个:数组numbers未填满,但文件已经结尾。那么fscanf将返回EOF指示文件结尾,并终止读取文件内容。 若文件中的字符串大于等于8个:数组numbers已填满,但文件内还有内容,这时没有地方再放置读取上来的数据了。也必须终止读取文件内容。

输出结果 1,1,1,1,1,1,1,1,-1,end of file 1 12 123 1234 12345 10 123456 1234567 以二进制形式读写 将数值以二进制形式保存

除了将数值转为字符串保存,数值还能不经过任何处理,直接以二进制形式保存成文件。下面介绍一个新函数fwrite,用于将数据直接写入到文件。

fwrite函数

size_t fwrite(const void* buffer, size_t size, size_t count, FILE* stream); 输入: const void *buffer待写入文件数据的首地址 size_t size每一块数据的大小 size_t count一共有多少块数据 FILE *stream文件结构指针 返回值: size_t成功写入多少块数据。

参数buffer

第一个参数 buffer 为待写入文件的数据的首地址。数组 numbers 出现在表达式中将会转为首元素指针,指向第一个int元素,类型为int *,其内部保存了数组的首地址。函数参数buffer为void *类型的指针,而void *类型的指针可以接收任何类型的指针。int *类型的指针在传递给void *类型的指针时,指针类型信息将丢失,仅留下首地址信息。

参数size与count

fwrite会把待写入数据分为count块,每一块size个字节。例如:

将数组numbers分为1块,每一块sizeof(numbers)大小。 将数组numbers分为8块,每一块sizeof(int)大小。

两种方式都能将整个数组写入文件,以下是对应的代码。

// 将数组numbers分为1块,每一块`sizeof(numbers)`大小 fwrite(numbers, sizeof(numbers), 1, pFile); // 将数组numbers分为8块,每一块sizeof(int)大小 fwrite(numbers, sizeof(int), 8, pFile);

而参数类型size_t为sizeof关键词返回值的类型,通常是unsigned int类型的别名。

FILE *stream

参数stream为使用fopen函数打开文件时返回的文件结构指针。

返回值

fwrite将返回成功写入文件的数据块的数量。

若将数组numbers分为1块,写入成功将返回1,写入失败将返回0。 若将数组numbers分为8块,写入成功将返回8,部分成功将返回小于8大于0的数值,写入失败将返回0。 二进制模式

字节0A是数值int类型的数值0A 00 00 00的前1个字节,刚好为\n的ASCII码。在文本模式下,字符\n将会被自动替换为\n\r ,再输出到文件中。其ASCII码为十六进制0D 0A。因此,数据0A 00 00 00前会出现一个OD。很显然,这里的字节0A并不代表换行,而是与其他3个十六进制字节一起表示一个int类型的数据。因此,以二进制形式存储为文件并不需要做这个转换。 默认情况下,文件是以文本模式打开的,文本模式下会做换行符的转换。而在函数fopen的第二个参数中,添加字符b。以二进制模式打开文件,二进制模式不进行换行符的转换。 FILE* pFile = fopen("data.txt", "w"); 修改为 FILE* pFile = fopen("data.txt", "wb"); 现在,字节0A前不会再自动添加0D了。

从文件中读取二进制

与之前讨论的直接将数据写入文件的fwrite函数对应,fread函数可以将文件中的数据直接读取到内存当中。由于现在需要读取文件,函数fopen的第二个参数,文件打开模式改为r。 size_t fread(void* buffer, size_t size, size_t count, FILE* stream); 输入: const void* buffer接收数据的首地址 size_t size每一块数据的大小 size_t count一共有多少块数据 FILE* stream文件结构指针 返回值 : size_t成功读取多少块数据。 函数fread的各个参数用法类似于fwrite函数,不同的是将写入换成了读取。它将从文件中读取count块数据,每一块数据size大小,读取出来的数据存放到buffer为首地址的空间中。返回值为成功读取的块的数量。

#include int main() { // 创建一个名为data.txt的文件 FILE* pFile = fopen("data.txt", "r"); if (pFile == NULL) { // 文件创建失败 return -1; } // 接收数值的数组 int numbers[8] = { 0 }; // 每块读取sizeof(numbers)字节,一共读取1块 fread(numbers, sizeof(numbers), 1, pFile); for (int i = 0; i < 8; i++) printf("%d\n", numbers[i]); // 关闭文件 fclose(pFile); return 0; }

除了读取固定大小的数据,我们也能让fread每次读取一字节数据,直到文件结尾或接收的空间存满为止。

#include void fileEofOrError(FILE* pFile) { if (feof(pFile) != 0) // 测试文件是否结尾 { printf("end of file\n"); } else if (ferror(pFile) != 0) // 测试文件是否读写出错 { printf("file access error\n"); } } int main() { // 创建一个名为data.txt的文件 FILE* pFile = fopen("data.txt", "r"); if (pFile == NULL) { // 文件创建失败 return -1; } // 接收数据的数组 int numbers[8] = { 0 }; // 接收数据的首地址 char* p = (char*)(numbers); // 已读取的字节 int count = 0; while (1) { // 如果数组已经填满8个元素,则不继续读取 if (count >= sizeof(numbers)) { printf("numbers is full\n"); break; } // 每块读取1字节,一共读取1块 int get = fread(p, 1, 1, pFile); if (get == EOF) { fileEofOrError(pFile); break; } p++; count++; } for (int i = 0; i < 8; i++) printf("%d\n", numbers[i]); // 关闭文件 fclose(pFile); return 0; }

由于fread函数每次读取1字节并存放到第一个参数指示的地址当中。因此,在下一次读取前,需要将接收数据的地址向后移动一字节。我们将数组首地址存放到一个char *类型的指针p当中。fread函数将读取到的1字节数据,存放到指针 p 中保存的地址当中。在下一次读取开始前,让指针p++,使得指针中保存的地址向后移动1字节。 注意,文件中的数据可能超过numbers数组的长度,因此,需要在程序中判断已读取到的数据大小。若数组已经装满,也不应该继续读取了,否则会造成数组越界。代码中使用count记录已经读取到的数据大小,当count大于数组长度sizeof(numbers)时,读取应当停止。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3