C语言readfile函数如何正确使用?

99ANYc3cd6
预计阅读时长 19 分钟
位置: 首页 C语言 正文

在标准的 C 语言库函数中,并没有一个叫做 readfile() 的函数。

你可能是在其他语言(如 PHP)中看到了这个函数名,或者是在某个特定的 C 库或框架中自定义的函数,在标准 C 中,文件操作是通过一组以 f(file)开头的函数来完成的,fopen(), fread(), fgets(), fgetc() 等。

下面,我将为你详细介绍标准 C 语言中读取文件的各种方法和最佳实践。


核心概念:文件指针

在 C 语言中,所有文件操作都通过一个指向 FILE 结构体的指针来完成,这个指针被称为文件指针,你可以把它想象成一个书签,它记录了当前在文件中的读写位置。

在开始任何文件操作之前,你必须使用 fopen() 函数打开文件,它会返回这个文件指针。

FILE *fopen(const char *filename, const char *mode);
  • filename: 你要打开的文件名(可以包含路径)。
  • mode: 打开文件的模式,
    • "r": 以只读模式打开文件,文件必须存在。
    • "w": 以只写模式打开文件,如果文件存在,则清空内容;如果不存在,则创建新文件。
    • "a": 以追加模式打开文件,如果文件存在,则在末尾写入;如果不存在,则创建新文件。
    • "r+": 以读写模式打开文件,文件必须存在。
    • "w+": 以读写模式打开文件,如果文件存在,则清空内容;如果不存在,则创建新文件。
    • "a+": 以读写模式打开文件,如果文件存在,则在末尾写入和读取;如果不存在,则创建新文件。

文件读取的几种主要方法

根据不同的需求,你可以选择不同的函数来读取文件内容。

fread() - 读取二进制数据或固定大小的块

fread() 是最通用的读取函数,它从一个文件中读取一个数据块。

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
  • ptr: 一个指向内存缓冲区的指针,用于存放从文件中读取的数据。
  • size: 每个数据项的大小(单位是字节)。
  • nmemb: 要读取的数据项的数量。
  • stream: 文件指针。
  • 返回值: 成功读取的数据项的数量,如果到达文件末尾或发生错误,则可能小于 nmemb

示例:读取整个文件到内存

#include <stdio.h>
#include <stdlib.h> // for exit()
int main() {
    FILE *fp;
    char *filename = "my_data.bin";
    char *buffer;
    long file_size;
    // 1. 打开文件
    fp = fopen(filename, "rb"); // "rb" 表示二进制读取模式
    if (fp == NULL) {
        perror("Error opening file");
        return 1;
    }
    // 2. 获取文件大小
    fseek(fp, 0, SEEK_END);
    file_size = ftell(fp);
    rewind(fp); // 将文件指针重置到开头
    // 3. 分配内存
    buffer = (char *)malloc(file_size * sizeof(char));
    if (buffer == NULL) {
        perror("Memory allocation failed");
        fclose(fp);
        return 1;
    }
    // 4. 读取文件
    size_t result = fread(buffer, 1, file_size, fp);
    if (result != file_size) {
        perror("Error reading file");
        free(buffer);
        fclose(fp);
        return 1;
    }
    // 5. buffer 中就包含了整个文件的内容
    printf("Successfully read %ld bytes from file.\n", file_size);
    // 在这里处理 buffer 中的数据...
    // 打印前100个字节
    for (int i = 0; i < 100 && i < file_size; i++) {
        printf("%c", buffer[i]);
    }
    printf("\n");
    // 6. 释放内存并关闭文件
    free(buffer);
    fclose(fp);
    return 0;
}

fgets() - 按行读取文本

fgets() 函数从文件中读取一行,直到遇到换行符 \n、文件结束符 EOF,或者读取了指定的字符数 - 1。

char *fgets(char *str, int n, FILE *stream);
  • str: 一个字符数组,用于存放读取的行。
  • n: 要读取的最大字符数(包括结尾的 \0)。
  • stream: 文件指针。
  • 返回值: 成功时返回 str 指针;如果到达文件末尾或发生错误,则返回 NULL

示例:逐行读取文本文件

#include <stdio.h>
int main() {
    FILE *fp;
    char filename[] = "my_text.txt";
    char buffer[256]; // 每行最多读取255个字符
    fp = fopen(filename, "r");
    if (fp == NULL) {
        perror("Error opening file");
        return 1;
    }
    printf("--- File Content ---\n");
    while (fgets(buffer, sizeof(buffer), fp) != NULL) {
        // fgets 会保留行尾的换行符 \n
        printf("%s", buffer);
    }
    fclose(fp);
    return 0;
}

fgetc() - 逐个字符读取

fgetc() 函数从文件中读取一个字符。

int fgetc(FILE *stream);
  • stream: 文件指针。
  • 返回值: 返回读取的字符(作为 int 类型),如果到达文件末尾或发生错误,则返回 EOF

示例:逐个字符读取并统计行数

#include <stdio.h>
int main() {
    FILE *fp;
    char filename[] = "my_text.txt";
    int c;
    int line_count = 0;
    fp = fopen(filename, "r");
    if (fp == NULL) {
        perror("Error opening file");
        return 1;
    }
    while ((c = fgetc(fp)) != EOF) {
        if (c == '\n') {
            line_count++;
        }
    }
    // 注意:如果文件最后一行没有换行符,行数会少一
    // 更精确的做法是检查第一个字符是否为EOF
    rewind(fp);
    if (fgetc(fp) != EOF) {
        line_count++; // 确保至少有一行
    }
    printf("The file has %d lines.\n", line_count);
    fclose(fp);
    return 0;
}

关键的辅助函数

  1. *`feof(FILE stream)`**

    • 作用: 检查文件指针是否到达了文件的末尾。
    • 返回值: 如果到达末尾,返回非零值(真);否则返回 0(假)。
    • 重要提示: feof()读取尝试失败后才会返回真,当 fread()fgets() 因为已经到达文件末尾而返回时,feof() 才会返回真,你不能用它来预判是否还有内容。
  2. *`ferror(FILE stream)`**

    • 作用: 检查文件流上是否发生了错误。
    • 返回值: 如果发生错误,返回非零值(真);否则返回 0(假)。
  3. *`clearerr(FILE stream)`**

    • 作用: 清除文件流的文件结束标志和错误标志。

最佳实践:一个健壮的文件读取循环

在读取文件时,正确的循环判断至关重要,推荐使用 freadfgets 的返回值作为循环条件,并在循环体内用 feofferror 来诊断问题。

// 使用 fgets 的健壮循环
FILE *fp = fopen("file.txt", "r");
if (!fp) { /* 错误处理 */ }
char line[1024];
while (fgets(line, sizeof(line), fp)) {
    // 成功读取了一行,处理 line
    printf("Read line: %s", line);
}
// 循环结束后,检查原因
if (feof(fp)) {
    printf("Reached end of file.\n");
} else if (ferror(fp)) {
    perror("An error occurred while reading the file.");
}
fclose(fp);

如何选择合适的函数?

场景 推荐函数 原因
读取整个文件到内存 fread() + fseek/ftell 效率高,一次调用即可完成,适合二进制文件或大文本文件。
逐行处理文本文件 fgets() 最简单、最安全的方式,能正确处理不同平台的换行符。
逐个字符处理 fgetc() 适用于需要检查每个字符的特殊逻辑(如统计字符、词法分析等)。
已知固定大小的数据结构 fread() 例如读取一个包含多个学生信息的结构化数组,fread 一次可以读入一个或多个结构体。

每次使用 fopen() 打开文件后,在程序结束前都必须使用 fclose() 关闭它,以确保所有缓冲区的数据都被正确写入磁盘,并释放系统资源。

-- 展开阅读全文 --
头像
织梦删除手机端跳转代码
« 上一篇 02-03
如何修改dede文章发布时间?
下一篇 » 02-03
取消
微信二维码
支付宝二维码

目录[+]