【C语言】标准 I/O 函数:fopen()、fread()、fwrite()等函数原型、参数说明以及返回值描述

以下是C标准库中常用文件I/O函数,定义在头文件 <stdio.h> 中,适用于文本和二进制文件的读写。它们是 ANSI C 标准的一部分,在大多数操作系统(如 Windows、Linux、macOS)下都支持。


1. fopen

FILE *fopen(const char *filename, const char *mode);
  • 参数:
    • filename:要打开的文件名(路径)。
    • mode:打开模式,例如 "r"(读)、"w"(写)、"a"(追加)、"rb"(二进制读)等。
  • 返回值:
    • 成功时返回指向 FILE 的指针;
    • 失败返回 NULL

2. fclose

int fclose(FILE *stream);
  • 参数:
    • stream:之前通过 fopen() 打开的文件指针。
  • 返回值:
    • 成功返回 0
    • 出错返回 EOF(通常是 -1)。

3. fread

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
  • 参数:
    • ptr:指向存储读取数据的缓冲区。
    • size:每个元素的大小(字节数)。
    • nmemb:要读取的元素个数。
    • stream:文件指针。
  • 返回值:
    • 成功读取的完整元素数量(小于等于 nmemb);
    • 若发生错误或到达文件末尾可能返回更少。

4. fwrite

size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
  • 参数:
    • ptr:指向要写入的数据的指针。
    • size:每个元素的大小。
    • nmemb:要写入的元素个数。
    • stream:文件指针。
  • 返回值:
    • 成功写入的完整元素数量;
    • 如果小于 nmemb,可能发生了错误。

5. fgetc

int fgetc(FILE *stream);
  • 参数:
    • stream:文件指针。
  • 返回值:
    • 成功返回读取到的字符(转换为 unsigned char 后转成 int);
    • 出错或文件结束返回 EOF

6. fgets

char *fgets(char *s, int n, FILE *stream);
  • 参数:
    • s:用于存储读取内容的字符串。
    • n:最多读取 n - 1 个字符(留一个给 \0)。
    • stream:文件指针。
  • 返回值:
    • 成功返回 s
    • 文件结束或出错返回 NULL

7. fputc

int fputc(int c, FILE *stream);
  • 参数:
    • c:要写入的字符(转换为 unsigned char 后作为 int 传递)。
    • stream:文件指针。
  • 返回值:
    • 成功返回写入的字符;
    • 出错返回 EOF

8. fputs

int fputs(const char *s, FILE *stream);
  • 参数:
    • s:要写入的字符串(不包括结尾的 \0)。
    • stream:文件指针。
  • 返回值:
    • 成功返回非负整数;
    • 出错返回 EOF

9. fprintf

int fprintf(FILE *stream, const char *format, ...);
  • 参数:
    • stream:文件指针。
    • format:格式化字符串。
    • 可变参数列表,对应 format 中的格式符。
  • 返回值:
    • 成功返回写入的字符数;
    • 出错返回负值。

10. fscanf

int fscanf(FILE *stream, const char *format, ...);
  • 参数:
    • stream:文件指针。
    • format:格式化字符串。
    • 可变参数列表,用于接收读取的数据。
  • 返回值:
    • 成功匹配并赋值的输入项数量;
    • 出错或文件结束返回 EOF

11. feof

int feof(FILE *stream);
  • 参数:
    • stream:文件指针。
  • 返回值:
    • 如果已到达文件末尾,返回非零值;
    • 否则返回 0。

12. ferror

int ferror(FILE *stream);
  • 参数:
    • stream:文件指针。
  • 返回值:
    • 如果流上发生错误,返回非零值;
    • 否则返回 0。

13. rewind

void rewind(FILE *stream);
  • 参数:
    • stream:文件指针。
  • 返回值:
    • 无返回值。将文件指针移动到文件开头,并清除错误标志。

14. ftell

long ftell(FILE *stream);
  • 参数:
    • stream:文件指针。
  • 返回值:
    • 返回当前文件指针的位置(从文件开始处算起的字节数);
    • 出错返回 -1L

15. fseek

int fseek(FILE *stream, long offset, int whence);
  • 参数:
    • stream:文件指针。
    • offset:偏移量(字节数)。
    • whence:起点位置,如 SEEK_SET(文件开头)、SEEK_CUR(当前位置)、SEEK_END(文件末尾)。
  • 返回值:
    • 成功返回 0
    • 出错返回非零值。

✅ 示例代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 定义一个简单的结构体
typedef struct {
    int id;
    char name[32];
    float salary;
} Employee;

int main() {
    FILE *bin_file = fopen("employees.dat", "wb+");
    FILE *txt_file = fopen("data.txt", "w+");

    if (!bin_file || !txt_file) {
        perror("文件打开失败");
        return EXIT_FAILURE;
    }

    // ========== fwrite + fread 示例 ==========
    Employee emp1 = {101, "Alice", 8000.0f};
    Employee emp2 = {0};

    // 写入结构体到二进制文件
    fwrite(&emp1, sizeof(Employee), 1, bin_file);
    rewind(bin_file);  // 回到文件开头

    // 读取结构体
    fread(&emp2, sizeof(Employee), 1, bin_file);

    printf("从二进制文件读取的数据:\n");
    printf("ID: %d, Name: %s, Salary: %.2f\n", emp2.id, emp2.name, emp2.salary);

    // 检查是否有错误
    if (ferror(bin_file)) {
        printf("二进制文件读写出错。\n");
        clearerr(bin_file); // 清除错误标志
    }

    // ========== fprintf + fscanf 示例 ==========
    // 写入格式化文本数据
    fprintf(txt_file, "%d %s %.2f\n", emp1.id, emp1.name, emp1.salary);
    rewind(txt_file);  // 回到文件开头

    // 读取格式化文本数据
    Employee emp3 = {0};
    fscanf(txt_file, "%d %s %f", &emp3.id, emp3.name, &emp3.salary);

    printf("\n从文本文件读取的数据:\n");
    printf("ID: %d, Name: %s, Salary: %.2f\n", emp3.id, emp3.name, emp3.salary);

    if (ferror(txt_file)) {
        printf("文本文件读写出错。\n");
    }

    // 关闭文件
    fclose(bin_file);
    fclose(txt_file);

    return 0;
}

🔍 输出结果(示例)

从二进制文件读取的数据:
ID: 101, Name: Alice, Salary: 8000.00

从文本文件读取的数据:
ID: 101, Name: Alice, Salary: 8000.00

<think>我们正在讨论文件描述符与C语言标准I/O函数(如fopen)的区别和联系。根据用户提供的引用和之前的问题,我们可以总结如下: ###区别: 1. **抽象层次不同**:- **文件描述符(fd)**:是操作系统内核提供的低级I/O接口(系统调用层),直接由内核管理。它用一个非负整数代表打开的文件,操作包括`open()`、`read()`、`write()`、`close()`等[^1][^2]。- **标准I/O函数(如fopen)**:是C语言标准库提供的高级I/O接口,围绕**流(stream)**的概念设计。它返回一个`FILE*`指针(文件指针),操作包括`fopen()`、`fread()`、`fwrite()`、`fclose()`等[^2][^3]。 2. **缓冲机制**:-**文件描述符**:默认无缓冲(或需手动设置缓冲),每次`read()`/`write()`都直接触发系统调用,频繁操作可能效率较低[^3]。- **标准I/O**:自带缓冲区管理(全缓冲/行缓冲/无缓冲),减少系统调用次数。例如,`fprintf()`先写入内存缓冲区,满时才调用`write()`,提升效率[^3]。 3. **操作对象**:-**文件描述符**:直接操作整数fd,适用于所有文件类型(普通文件、管道、套接字等)。- **标准I/O**:操作`FILE*`结构体,内部封装了fd和缓冲区信息,仅适用于普通文件(但可通过`fdopen()`将fd转为`FILE*`)[^2][^3]。 4. **错误处理**:-**文件描述符**:通过返回值`-1`和全局变量`errno`报错(如`EBADF`表示无效fd)。-**标准I/O**:通过返回值(如`NULL`)和`ferror()`/`feof()`函数报错,更易用[^3]。 ###联系: 1. **底层依赖**:标准I/O库的流(`FILE*`)内部封装了文件描述符。例如:```cFILE*fp= fopen("file.txt","r");intfd =fileno(fp);//从FILE*获取底层fd```反之,可通过`fdopen()`将fd转为`FILE*`:```cint fd= open("file.txt",O_RDONLY);FILE*fp= fdopen(fd,"r"); //将fd转为流```2.**功能对应**:标准I/O函数与系统调用功能相似但有缓冲优化:|系统调用(fd) |标准I/O(FILE*) |作用||----------------|------------------|------||`read()`| `fread()`|读取数据|| `write()`|`fwrite()`|写入数据 ||`open()`| `fopen()`|打开文件|| `close()`|`fclose()`|关闭文件 |3.**性能互补**:-标准I/O的缓冲区机制适合频繁小数据读写(如逐字符`fgetc()`)。-直接使用fd适合大块数据或实时性要求高的场景(如网络套接字)[^3]。 ###总结: -**区别本质**:文件描述符是**系统级**原始接口,标准I/O是**语言级**带缓冲的封装。- **协作场景**:在需要混合使用高低级I/O时(如用`open()`打开文件后转为`FILE*`缓冲读写),二者可通过`fileno()`和`fdopen()`互转[^2][^3]。---###示例:混合使用fd与FILE* ```c#include <stdio.h> #include<fcntl.h>#include <unistd.h> intmain(){//使用系统调用打开文件intfd =open("data.txt", O_RDWR);//将fd转为FILE*流以使用标准I/OFILE*fp= fdopen(fd,"r+");fprintf(fp,"Hellovia FILE*\n"); //缓冲写入fflush(fp); //确保数据写入内核//直接通过fd写入write(fd,"Hellovia fd\n",13);fclose(fp); //关闭流时会自动关闭底层fdreturn0; }```§§相关问题§§1.标准I/O的缓冲区大小如何设置?对性能有何影响?2.为什么网络编程中通常直接使用文件描述符而非标准I/O? 3.在多线程环境下,标准I/O和文件描述符操作哪个更安全?</think>### 文件描述符与C语言标准I/O函数的区别与联系 #### 核心区别 1. **抽象层级不同** - **文件描述(fd)**:操作系统内核提供的**低级I/O接口**(系统调用层) - 直接对应内核文件表项 - 使用整数标识符(如0,1,2) - 操作函数:`open()`, `read()`, `write()`, `close()` - **标准I/O函数**:C标准库提供的**高级I/O抽象**(用户层) - 围绕`FILE*`流对象操作 - 操作函数:`fopen()`, `fread()`, `fwrite()`, `fclose()` - 内部封装了文件描述符[^3] 2. **缓冲机制差异** | 特性 | 文件描述符 | 标准I/O | |---|---|---| | 缓冲区 | 无缓冲(直接系统调用) | 自带缓冲区管理 | | 效率 | 小数据操作效率低 | 减少系统调用次数 | | 控制 | 需手动管理 | 自动缓冲(可设置模式) | ```c // 文件描述符:每次read都是系统调用 read(fd, buf, 1); // 触发1次内核调用 // 标准I/O:缓冲到内存后批量处理 fgetc(fp); // 可能缓存1024字节后处理 ``` 3. **错误处理方式** - 文件描述符:通过返回值`-1`和`errno`获取错误 - 标准I/O:通过`ferror()`, `feof()`等函数检测状态[^3] 4. **功能扩展性** - 文件描述符:支持所有I/O类型(管道、套接字等) - 标准I/O:主要针对普通文件(但可通过`fdopen()`转换) #### 核心联系 1. **底层依赖关系** 标准I/O库内部使用文件描述符实现: ```c FILE *fp = fopen("file.txt", "r"); int fd = fileno(fp); // 从FILE*获取底层文件描述符 ``` 每个`FILE*`结构体都包含一个文件描述符字段[^3] 2. **转换接口** - `fdopen()`:将文件描述符转换为`FILE*`流 ```c int fd = open("file.txt", O_RDWR); FILE *fp = fdopen(fd, "r+"); // 转换为标准I/O流 ``` - `fileno()`:从`FILE*`获取底层文件描述符[^2] 3. **性能互补** | 场景 | 推荐使用 | 原因 | |---|---|---| | 高频小数据读写 | 标准I/O | 缓冲区减少系统调用 | | 大块数据传输 | 文件描述符 | 避免双重缓冲开销 | | 网络编程 | 文件描述符 | 直接控制非阻塞I/O | #### 典型应用对比 | 操作类型 | 文件描述符 | 标准I/O | |---|---|---| | 打开文件 | `open()` | `fopen()` | | 字符读写 | `read()`/`write()` | `fgetc()`/`fputc()` | | 行处理 | 需手动实现 | `fgets()`/`fputs()` | | 格式化I/O | 不支持 | `fprintf()`/`fscanf()` | | 二进制I/O | `read()`/`write()` | `fread()`/`fwrite()`[^1] | #### 总结图示 ```mermaid graph LR A[应用程序] --> B[标准I/O库 fopen/fread] B --> C[文件描述符 open/read] C --> D[内核文件表] D --> E[物理文件] style B fill:#cff,stroke:#333 style C fill:#f9f,stroke:#333 ``` - **抽象链**:应用程序 → 标准I/O流(`FILE*`) → 文件描述(fd) → 内核对象 → 物理文件 - **标准I/O**增加缓冲层提升效率 - **文件描述符**提供最终的系统级访问
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值