Skip to content

C语言文件I/O详解

文件I/O是C语言中常用的操作之一,它是对磁盘中存储数据的读取与写入。文件I/O的操作由C语言的标准库提供,具体通过 fopenfreadfwritefclose 等函数实现。本文将详细介绍C语言文件I/O相关的常见问题,并对常见的易混淆点进行澄清。

1. fopenopen 的区别与联系

在Unix/Linux系统下,文件I/O有两套接口:fopen/fread/fwrite系列和 open/read/write系列,它们之间有一些区别和联系:

  • fopen 是C标准库提供的接口,几乎所有的C开发环境都支持它。
  • open 是POSIX标准接口,只有符合POSIX标准的操作系统(如Linux)才支持。

区别:

  • fopen 返回一个 FILE * 类型的指针,表示打开的文件。
  • open 返回一个文件描述符 fdint 类型),这个文件描述符在操作系统中用于标识一个已打开的文件。

从可移植性的角度来看,建议使用 fopen,因为它是C标准库的一部分,适用于所有符合C标准的系统。

freadfwrite 的缓存机制:

  • freadfwrite 具有内部缓存机制,可以减少系统调用次数,提高效率。每次调用 freadfwrite 时,操作的是缓冲区,不直接进行磁盘操作。
  • 相比之下,readwrite 没有内部缓冲,频繁调用会导致频繁的系统调用,效率较低。

open 除了用于文件,还可以用于操作其他资源:

在Linux中,open 除了用于文件,还可用于套接字、管道等设备。fopen 仅用于文件操作。

2. freadfwrite 的参数

freadfwrite 的函数原型如下:

c
size_t fread(void *buffer, size_t size, size_t count, FILE *fp);
size_t fwrite(const void *buffer, size_t size, size_t count, FILE *fp);
  • buffer:读取或写入数据的缓冲区。
  • size:每个记录的大小(单位:字节)。
  • count:要读取或写入的记录数。
  • fp:文件指针。

freadfwrite 是以“记录”为单位进行读写的,而不是字节。size 表示每个记录的大小,count 表示要读写的记录数量。

示例:读取和写入结构体

假设有一个结构体 T

c
struct T {
    int id;
    char name[50];
};

读取结构体数组时应该使用以下方式:

c
fread(buf, sizeof(T), 10, fp);

这表示每次读取一个 T 类型的结构体,并读取 10 个结构体。

注意:sizecount 的选择

  • 如果 fread(buf, 4096, 1, fp) 读取失败,它会返回 NULL,因为文件大小可能不够填满整个缓冲区。
  • fread(buf, 1, 4096, fp) 会成功读取文件中的所有数据(最多 4000 字节)。

3. fseek 操作后的恢复

fseek 操作用于改变文件读写位置。当你需要获取文件的大小时,通常会使用以下方法:

c
int get_filesize(FILE *fp) {
    fseek(fp, 0, SEEK_END);  // 移动到文件末尾
    long f_len = ftell(fp);  // 获取文件大小
    return f_len;
}

但是,fseek 会改变文件指针的位置。如果你不恢复文件指针的位置,后续的读取或写入操作可能会受到影响。因此,在获取文件大小后,应该恢复文件指针的位置:

c
fseek(fp, 0, SEEK_SET);  // 恢复到文件开头

4. 忘记 fclose 的影响

fclose 用于关闭文件。当忘记调用 fclose 时,会导致以下问题:

  • 文件描述符泄漏:每个打开的文件都会占用一个文件描述符,忘记关闭文件会导致文件描述符泄漏,直到程序退出。如果所有的文件描述符被占用,后续的文件操作将会失败。
  • 缓冲区未刷新fclose 会确保所有的缓冲数据被写入文件。如果忘记调用 fclose,缓冲区中的数据可能没有写入文件,导致数据丢失。

5. fopenrrb 模式

C语言中的 fopen 支持以不同模式打开文件:

  • r:以文本模式打开文件,只适用于文本文件。
  • rb:以二进制模式打开文件,适用于二进制文件。

文本文件与二进制文件的区别

  • 文本文件:通常包含字符(如 ASCII 或 Unicode 编码),在不同的系统中,换行符的表示方式可能不同。例如,Windows 使用 \r\n(回车换行),而Linux使用 \n(换行)。fopenr 模式会自动处理这些特殊字符。
  • 二进制文件:文件内容是按字节存储的,不能像文本文件那样直接解码成字符。读取二进制文件时,必须使用 rb 模式,以确保文件内容不被修改。

常见错误

  • 文本文件与二进制文件混用:如果用 r 模式打开二进制文件,可能导致换行符转换错误,导致文件内容损坏。
  • EOF 和 feof 的混用:在判断文件结尾时,应正确使用 EOFfeoffeof 检查文件是否到达末尾,而 EOF 用于表示读操作失败。

总结

C语言的文件I/O操作非常强大,但同时也容易出错。了解不同函数的工作原理和细节,有助于提高代码的稳定性和性能。最常见的问题通常来源于文件指针操作、文件模式选择以及缓冲区管理。正确理解 fopenfreadfwritefseek 的用法,有助于避免常见的错误。

基于 MIT 许可发布