【C语言】文件读取方法基础

梳理一下 C 语言中文件读取方法的基础语法和要点。

核心概念

在 C 语言中,文件操作是通过 文件指针 (File Pointer) 和 C 标准库 (stdio.h) 提供的一系列函数来完成的。基本流程如下:

定义文件指针: 使用 FILE * 类型定义一个指针变量。

打开文件: 使用 fopen() 函数打开一个物理文件,并将其与文件指针关联起来。你需要指定文件名和 打开模式 (例如,只读)。

读取数据: 使用不同的读取函数(如 fgetc, fgets, fscanf, fread)从文件中读取数据。

处理数据: 对读取到的数据进行你需要的操作。

判断文件结束: 在读取循环中,需要判断是否已经到达文件末尾 (EOF - End Of File)。

关闭文件: 使用 fclose() 函数关闭文件,释放资源。

错误处理: 在打开文件和读取/写入过程中,需要检查可能发生的错误。

基础语法和关键函数

1. 包含头文件

进行文件操作前,必须包含标准输入输出头文件:

#include

2. 定义文件指针

FILE *fp; // 或者 FILE *infile, *pFile 等有意义的名称

FILE 是一个在 stdio.h 中定义的结构体类型,用于存储文件的相关信息(如缓冲区、当前位置、错误状态等)。我们通过操作指向 FILE 结构的指针来进行文件操作。

3. 打开文件 (fopen)

fp = fopen("文件名", "打开模式");

文件名: 一个字符串,表示要打开的文件的路径和名称(例如 "data.txt", "C:\\Users\\Me\\Documents\\input.log")。

打开模式: 一个字符串,指定文件的打开方式。对于读取,常用的模式有:

"r": 只读 (Read)。文件必须存在,否则打开失败。从文件开头读取。

"rb": 只读,二进制模式 (Read Binary)。同上,但用于读取二进制文件(如图片、音频)。

"r+": 读写 (Read Plus)。文件必须存在。可以读取和写入,从文件开头开始。

"rb+" 或 "r+b": 读写,二进制模式 (Read Binary Plus)。同上,用于二进制文件。

返回值:

成功:返回一个指向 FILE 结构的指针 (即文件指针)。

失败:返回 NULL。 必须检查 fopen 的返回值!

示例:打开文件并检查错误

#include

#include // 为了 exit()

int main() {

FILE *fp;

const char *filename = "mydata.txt";

fp = fopen(filename, "r"); // 尝试以只读模式打开

if (fp == NULL) {

perror("Error opening file"); // perror 会打印错误信息和系统错误原因

// 或者 fprintf(stderr, "无法打开文件 %s\n", filename);

return 1; // 或者 exit(EXIT_FAILURE); // 表示程序异常退出

}

printf("文件 %s 打开成功!\n", filename);

// ... 接下来进行文件读取操作 ...

// 关闭文件(稍后讲解)

fclose(fp);

return 0; // 程序正常退出

}

4. 读取文件内容

有多种方法可以读取文件内容,根据需要选择:

a) 读取单个字符 (fgetc)

一次读取一个字符。

返回读取到的字符 (作为 int 类型)。

如果到达文件末尾或发生错误,返回 EOF (一个在 stdio.h 中定义的特殊整数常量,通常是 -1)。

#include

int main() {

FILE *fp;

int ch; // 注意是 int 类型,用来接收 fgetc 的返回值,包括 EOF

fp = fopen("mydata.txt", "r");

if (fp == NULL) {

perror("Error opening file");

return 1;

}

printf("文件内容:\n");

// 循环读取,直到 fgetc 返回 EOF

while ((ch = fgetc(fp)) != EOF) {

putchar(ch); // 将读取到的字符打印到屏幕

// 或者 (char)ch 进行处理

}

// 检查是读取结束还是发生错误 (可选但推荐)

if (ferror(fp)) {

perror("Error reading file");

} else if (feof(fp)) {

printf("\n文件读取结束 (EOF reached).\n");

}

fclose(fp);

return 0;

}

b) 读取一行字符串 (fgets)

读取一整行(直到换行符 \n)或指定的最大字符数。

推荐使用,因为它更安全,可以防止缓冲区溢出。

会在读取的字符串末尾自动添加空字符 \0。

会 读取并存储行末的换行符 \n (如果空间足够)。

char *fgets(char *str, int n, FILE *stream);

str: 用于存储读取内容的字符数组(缓冲区)。

n: 最多读取 n-1 个字符(留一个位置给 \0)。

stream: 文件指针。

返回值:

成功:返回 str (指向缓冲区的指针)。

到达文件末尾(且未读取任何字符)或发生错误:返回 NULL。

#include

#define BUFFER_SIZE 256 // 定义缓冲区大小

int main() {

FILE *fp;

char buffer[BUFFER_SIZE];

fp = fopen("mydata.txt", "r");

if (fp == NULL) {

perror("Error opening file");

return 1;

}

printf("文件内容 (按行读取):\n");

// 循环读取,直到 fgets 返回 NULL

while (fgets(buffer, BUFFER_SIZE, fp) != NULL) {

printf("%s", buffer); // 直接打印,因为 buffer 可能包含换行符

}

// 检查是读取结束还是发生错误 (可选但推荐)

if (ferror(fp)) {

perror("Error reading file");

} else if (feof(fp)) {

printf("\n文件读取结束 (EOF reached).\n");

}

fclose(fp);

return 0;

}

c) 按格式读取 (fscanf)

类似于 scanf,但从文件读取。

根据指定的格式字符串解析数据。

注意: 对于字符串读取,fscanf 遇到空白字符(空格、制表符、换行符)会停止,可能不适合读取整行文本。处理格式不固定的文本时要小心。

int fscanf(FILE *stream, const char *format, ...);

stream: 文件指针。

format: 格式控制字符串 (同 scanf)。

...: 接收数据的变量地址。

返回值:

成功:返回成功匹配并赋值的项数。

未匹配任何项(可能由于格式不符或到达文件末尾):返回 0 或 EOF。

发生读取错误:返回 EOF。

#include

// 假设 mydata.txt 内容是:

// Name John Age 30 Score 95.5

// Name Jane Age 25 Score 88.0

int main() {

FILE *fp;

char name_label[10], name[50], age_label[10];

int age;

char score_label[10];

double score;

fp = fopen("mydata.txt", "r");

if (fp == NULL) {

perror("Error opening file");

return 1;

}

printf("从文件读取结构化数据:\n");

// 循环读取,直到 fscanf 返回值不等于预期的项数 (这里是 6)

// 或者直接判断返回值是否等于 EOF

while (fscanf(fp, "%s %s %s %d %s %lf",

name_label, name, age_label, &age, score_label, &score) == 6)

{

printf("读取到: Name=%s, Age=%d, Score=%.1f\n", name, age, score);

}

// 检查是读取结束还是发生错误/格式不匹配

if (ferror(fp)) {

perror("Error reading file");

} else if (feof(fp)) {

printf("文件读取结束或格式不再匹配.\n");

} else {

// 如果循环是因为格式不匹配而停止的,这里会被执行

printf("文件格式不匹配或读取意外终止.\n");

}

fclose(fp);

return 0;

}

d) 读取二进制数据 (fread)

用于读取指定大小和数量的数据块,通常用于非文本文件(二进制文件)。

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

ptr: 指向用于存储读取数据的内存块的指针(缓冲区)。

size: 要读取的每个数据项的大小(字节)。

nmemb: 要读取的数据项的数量。

stream: 文件指针。

返回值: 返回成功读取的数据项的数量(不是字节数!)。如果发生错误或到达文件末尾,返回值可能小于 nmemb。

5. 关闭文件 (fclose)

断开文件指针与物理文件的连接。

将缓冲区中剩余的数据(如果有的话,主要针对写入)刷新到文件中。

释放与文件关联的系统资源。

必须在使用完文件后调用 fclose!

int fclose(FILE *stream);

stream: 要关闭的文件指针。

返回值:

成功:返回 0。

失败:返回 EOF (通常表示写入错误,如磁盘已满)。

#include

int main() {

FILE *fp;

fp = fopen("mydata.txt", "r");

if (fp == NULL) {

perror("Error opening file");

return 1;

}

// ... 读取操作 ...

if (fclose(fp) != 0) {

perror("Error closing file");

// 注意:即使关闭失败,文件指针 fp 也可能不再有效

} else {

printf("文件成功关闭。\n");

}

// fp = NULL; // 可选:将指针设为 NULL,防止后续误用

return 0;

}

6. 文件结束与错误检查

feof(FILE *stream): 检查文件流的文件结束指示符是否被设置。注意:feof 只有在一次读取操作尝试读取超出文件末尾之后才会返回真 (非零值)。不要用 while(!feof(fp)) 作为读取循环的条件,应该在读取操作失败后使用 feof 来判断失败的原因是否是到达文件末尾。

ferror(FILE *stream): 检查文件流的错误指示符是否被设置。如果之前的I/O操作发生了错误,返回真 (非零值)。

正确的文件读取循环模式:

// 使用 fgetc

int ch;

while ((ch = fgetc(fp)) != EOF) {

// 处理 ch

}

// 循环结束后检查是 EOF 还是 error

if (ferror(fp)) { /* handle error */ }

else { /* EOF reached */ }

// 使用 fgets

char buffer[SIZE];

while (fgets(buffer, SIZE, fp) != NULL) {

// 处理 buffer

}

// 循环结束后检查是 EOF 还是 error

if (ferror(fp)) { /* handle error */ }

else { /* EOF reached (or possibly partial read on last line) */ }

// 使用 fscanf (假设期望读取 N 个项目)

while (fscanf(fp, "...", ...) == N) {

// 处理读取到的数据

}

// 循环结束后检查是 EOF 还是 error/format mismatch

if (ferror(fp)) { /* handle error */ }

else if (feof(fp)) { /* EOF reached cleanly or after last successful match */ }

else { /* format mismatch */ }

// 使用 fread (假设期望读取 count 个项目)

size_t items_read;

while ((items_read = fread(buffer, item_size, count, fp)) == count) {

// 处理 buffer 中的 count 个项目

}

// 处理最后一次可能不完整的读取 (items_read < count)

if (items_read > 0) {

// 处理 buffer 中的 items_read 个项目

}

// 循环结束后检查是 EOF 还是 error

if (ferror(fp)) { /* handle error */ }

else { /* EOF reached */ }

要点总结

包含

使用 FILE * 定义文件指针。

使用 fopen() 打开文件,指定文件名和读取模式 ("r", "rb" 等)。

必须检查 fopen() 的返回值是否为 NULL。

根据需要选择合适的读取函数 (fgetc, fgets, fscanf, fread)。

读取循环的条件应基于读取函数的返回值,而不是 feof()。

在读取循环结束后,使用 feof() 和 ferror() 区分文件结束和读取错误。

对于 fgets,注意提供足够大的缓冲区并处理可能存在的行末换行符。

对于 fscanf,注意其处理空白字符的行为和返回值(匹配项数)。

使用 fclose() 关闭文件以释放资源,并检查其返回值。

始终进行错误处理。

Copyright © 2022 篮球世界杯_世界杯亚洲区名额 - cdbnfc.com All Rights Reserved.