C语言文件操作全解:从打开关闭到读写函数与错误处理

Ruby
Ruby
发布时间:2026-10-16 | 5人浏览过
原创

C语言中的文件操作涉及磁盘文件的读取与写入,主要分为程序文件和数据文件。本文将系统性梳理文件定义、FILE结构体、打开与关闭流程、各类读写函数的使用方法,以及如何准确判断文件结束和发生错误。

文件的定义与分类

在程序设计中,通常接触两类文件:

  1. 程序文件:包括源程序文件(后缀为.c)、目标文件(Windows下为.obj)和可执行文件(.exe)。
  2. 数据文件:文件的内容不一定是程序,而是供运行时读写的数据。例如程序运行需要从中读取数据的输入文件,或向其中输出内容的日志文件。

根据存储格式的不同,文件又分为:

  • 文本文件:数据以ASCII码形式存储,肉眼可直接阅读。
  • 二进制文件:数据在内存中按二进制形式直接存储到外存,若不进行转换,其内容无法直接阅读。

文件名的组成

为了唯一标识一个文件,文件名由三部分组成:文件路径 + 文件名主干 + 文件后缀。

例如:c:\code\test.txt

文件指针与FILE结构体

在内存中,每个被使用的文件都会开辟一个“文件信息区”,存放文件的名称、状态、当前位置等信息。这些信息保存在一个名为 FILE 的结构体变量中。

不同编译器的 FILE 结构体内部细节可能不同(如Visual Studio 2019中定义为指向占位符的结构),但使用者无需关心具体字段。我们通常通过 FILE* 类型的指针来维护这个结构体,从而关联并访问文件。

FILE *pf;

pf 是指向 FILE 类型数据的指针变量,通过它即可找到关联的文件及其信息区。

文件的打开与关闭

ANSI C标准规定使用 fopen 打开文件,使用 fclose 关闭文件。打开成功时,返回指向该文件的 FILE* 指针;失败则返回 NULL。

路径写法

相对路径(相对于当前代码所在目录):. 表示当前路径,.. 表示上一级路径。

FILE* p = fopen("../test.txt","r");

绝对路径需明确指定盘符,注意在字符串中转义反斜杠 \\。

fopen("C:\\2012_code\\84\\test\\test1.txt", "r");

打开方式(mode)详解

文件打开方式决定了后续的操作权限及文件不存在时的行为:

模式 含义 若文件不存在
"r"(只读) 打开已存在的文本文件 出错,返回 NULL
"w"(只写) 打开用于输出的文本文件 创建新文件,原有内容清空
"a"(追加) 向文本文件尾部添加数据 创建新文件
"rb"(只读) 打开已存在的二进制文件 出错,返回 NULL
"wb"(只写) 打开用于输出的二进制文件 创建新文件,原有内容清空
"ab"(追加) 向二进制文件尾部添加数据 创建新文件
"r+"(读写) 打开用于读写的文本文件 出错,返回 NULL
"w+"(读写) 创建新文件用于读写 创建新文件
"a+"(读写) 打开或创建文件,末尾追加,也可读 创建新文件

安全检查

使用文件前必须进行空指针检查,以防打开失败导致程序崩溃:

FILE* pf = fopen("text.txt","r");
if(pf == NULL)
{
    printf("%s\n", strerror(errno)); // 输出错误原因
    return 0;
}
fclose(pf); // 操作完毕后关闭文件
pf = NULL;  // 置为空指针,防止悬垂引用

文件的顺序读写

常用的读写函数针对不同类型的流(stdin/stdout)或文件进行操作。键盘对应标准输入流 stdin,屏幕对应标准输出流 stdout,它们本质上也是 FILE 类型。

功能 函数原型 适用对象
字符输入 int fgetc(FILE* stream) 所有输入流
字符输出 int fputc(int c, FILE* stream) 所有输出流
文本行输入 char* fgets(char* s, int n, FILE* stream) 所有输入流
文本行输出 int fputs(const char* s, FILE* stream) 所有输出流
格式化输入 int fscanf(FILE* stream, ...) 所有输入流
格式化输出 int fprintf(FILE* stream, ...) 所有输出流
二进制输入 size_t fread(void* ptr, size_t size, size_t nmemb, FILE* stream) 文件/流
二进制输出 size_t fwrite(const void* ptr, size_t size, size_t nmemb, FILE* stream) 文件/流

fread返回值说明

fread 返回实际读取的项数(元素个数)。如果发生错误或到达文件末尾,返回的值可能小于请求的项数。建议使用 feof() 或 ferror() 区分是EOF还是错误。

格式化函数的对比

fscanf/fprintf 是在标准 scanf/printf 基础上增加文件指针参数,针对文件流进行操作。

sscanf/sprintf 则是针对内存中的字符串缓冲区(String Buffer)进行格式化读写,常用于记录日志或字符串处理。

文件随机访问

除了顺序读写,还可以利用 fseek 定位文件指针,实现随机访问。

fseek:定位文件指针

int fseek(FILE* stream, long offset, int origin);
  • offset:偏移量(字节为单位),正数向后,负数向前。
  • origin:起始位置,可选常量:SEEK_SET(文件头)、SEEK_CUR(当前位置)、SEEK_END(文件尾)。

ftell:获取当前位置

long ftell(FILE* stream);

返回当前文件指针相对于文件起始位置的字节偏移量。可用于计算文件大小(调用 fseek(pf, 0, SEEK_END) 后调用 ftell)。

rewind:重置指针

void rewind(FILE* stream);

相当于将文件指针重置到文件开头,即 fseek(stream, 0, SEEK_SET)。

文件结束判断与错误检测

这是C语言文件操作中最易出错的环节。不能用 feof() 的返回值直接作为循环结束条件,因为 feof() 只有在“尝试读取超过文件末尾”后才返回非零值。

正确判断文件结束的方法

  1. 文本文件:判断读取函数的返回值。
  • fgetc:返回 EOF 表示结束或错误。
  • fgets:返回 NULL 表示结束或错误。
  • fscanf:返回值小于要求的字段数表示结束。
  • 二进制文件:判断实际读取的元素数量是否小于请求的数量。
    • fread:返回值 < count 表示到达末尾。

    错误判断函数

    当需要区分“正常结束”和“异常错误”时,需结合使用以下函数:

    • perror():自动打印传入的字符串、冒号及操作系统对应的错误信息。
    • ferror(FILE*):若流发生错误返回非零值,否则返回0。
    • feof(FILE*):若到达文件末尾返回非零值,否则返回0。

    综合示例

    int main()
    {
    	FILE* pf = fopen("test.txt","r");
        if(pf == NULL)
        {
            perror("Open file failed");
            return 0;
        }
        
        int ch = 0;
        // 循环读取直到遇到 EOF 或错误
        while((ch = fgetc(pf)) != EOF)
        {
    		fputc(ch, stdout); // 将字符输出到屏幕
        }
        
        // 此时循环退出,判断是读完还是出错
        if(ferror(pf))
        {
    		printf("File read error occurred.\n");
        }
        else if(feof(pf))
        {
    		printf("Reached end of file normally.\n");
        }
        
        fclose(pf);
        pf = NULL;
        return 0;
    }
    
    以上就是C语言文件操作的详细内容,更多关于C语言文件操作的资料请关注本站其它相关文章!

    本站声明

    本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

    Python 相关产品和应用

    全部下载

    专题列表