ZetCode

C clearerr 函数

最后修改日期:2025 年 4 月 6 日

在 C 文件操作中,错误处理对于确保程序的健壮性和可靠性至关重要。clearerr 函数有助于有效管理文件流的错误指示器。本教程将深入探讨 clearerr,解释其目的、行为和实际应用。您将学习如何重置错误和 EOF 标志,以便在遇到问题后继续文件操作。掌握 clearerr 将提高您优雅处理文件 I/O 错误的能力。

什么是 clearerr?

clearerr 函数用于清除文件流的错误和文件结束(EOF)指示器。它接受一个参数:指向 FILE 对象的指针。调用时,它会重置错误和 EOF 标志,允许后续操作继续进行。当您遇到错误并希望重试操作时,此函数特别有用。请注意,clearerr 并不能修复导致错误的根本问题。

基本语法

clearerr 的函数声明非常简单

void clearerr(FILE *stream);

该函数不返回任何值,它只是清除指定流的错误状态。stream 参数必须指向一个有效的、已打开的文件流。调用 clearerr 后,您可以使用 feofferror 再次检查流的状态。

清除 EOF 指示符

此示例演示了如何使用 clearerr 重置 EOF 标志。

clear_eof.c
#include <stdio.h>

int main() {
    FILE *fp = fopen("test.txt", "r");
    if (fp == NULL) {
        perror("Failed to open file");
        return 1;
    }

    // Read past EOF
    int ch;
    while ((ch = fgetc(fp)) != EOF) {
        putchar(ch);
    }

    printf("\nEOF reached. feof(): %d\n", feof(fp));
    
    clearerr(fp);  // Clear EOF indicator
    printf("After clearerr. feof(): %d\n", feof(fp));

    fclose(fp);
    return 0;
}

此代码首先读取文件直到到达 EOF,这会设置 EOF 指示符。feof 函数通过返回非零值来确认这一点。调用 clearerr 后,feof 返回零,表明 EOF 标志已被清除。请注意,文件位置仍停留在 EOF,除非使用 fseekrewind 移动。

处理读取错误

此示例展示了如何在读取操作失败后清除错误条件。

read_error.c
#include <stdio.h>

int main() {
    FILE *fp = fopen("test.txt", "r");
    if (fp == NULL) {
        perror("Failed to open file");
        return 1;
    }

    // Simulate error by closing the file descriptor
    fclose(fp);
    
    int ch = fgetc(fp);  // This will fail
    if (ferror(fp)) {
        printf("Error occurred. ferror(): %d\n", ferror(fp));
        clearerr(fp);  // Clear the error
        printf("After clearerr. ferror(): %d\n", ferror(fp));
    }

    return 0;
}

在这里,我们通过尝试从已关闭的文件读取来故意引发错误。ferror 函数检测到此错误条件。调用 clearerr 后,ferror 返回零,表示错误标志已被清除。请记住,清除错误并不能使流在关闭后再次可用。

与 fseek 结合使用

此示例结合使用 clearerrfseek 来从错误中恢复。

fseek_clearerr.c
#include <stdio.h>

int main() {
    FILE *fp = fopen("data.bin", "rb+");
    if (fp == NULL) {
        perror("Failed to open file");
        return 1;
    }

    // Try to read from write-only stream (simulated error)
    int value;
    if (fread(&value, sizeof(int), 1, fp) != 1) {
        if (ferror(fp)) {
            printf("Read error occurred. Clearing...\n");
            clearerr(fp);
        }
    }

    // Reset position and try writing
    fseek(fp, 0, SEEK_SET);
    value = 42;
    if (fwrite(&value, sizeof(int), 1, fp) != 1) {
        perror("Write failed");
    }

    fclose(fp);
    return 0;
}

此代码尝试从可能是只写流(取决于文件权限)中读取。在用 ferror 检测到错误后,它用 clearerr 清除错误状态,并使用 fseek 重置文件位置。随后的写入操作然后正常进行。这表明了如何从某些类型的错误中恢复。

循环中的错误恢复

此示例演示了在读取循环中使用 clearerr 进行健壮的错误处理。

error_recovery.c
#include <stdio.h>
#include <unistd.h>

int main() {
    FILE *fp = fopen("input.txt", "r");
    if (fp == NULL) {
        perror("Failed to open file");
        return 1;
    }

    char buffer[256];
    int attempts = 3;
    
    while (attempts-- > 0) {
        if (fgets(buffer, sizeof(buffer), fp) == NULL) {
            if (feof(fp)) {
                printf("End of file reached\n");
                break;
            } else if (ferror(fp)) {
                printf("Read error (attempts left: %d)\n", attempts);
                clearerr(fp);
                sleep(1);  // Wait before retry
                continue;
            }
        }
        printf("Read: %s", buffer);
    }

    fclose(fp);
    return 0;
}

此代码在发生读取错误时实现了重试机制。如果遇到错误,它会尝试最多重试读取三次。每次出错后,它都会使用 clearerr 重置错误状态,然后再进行重试。sleep 调用模拟了一个延迟,这在实际场景(例如等待网络资源)中可能有用。这种模式对于处理瞬时错误非常有用。

在清除之前检查错误状态

此示例通过在清除错误状态之前进行检查来展示良好实践。

check_before_clear.c
#include <stdio.h>

int main() {
    FILE *fp = fopen("test.txt", "r");
    if (fp == NULL) {
        perror("Failed to open file");
        return 1;
    }

    // Read until EOF
    while (fgetc(fp) != EOF);

    printf("Before clearerr:\n");
    printf("feof(): %d, ferror(): %d\n", feof(fp), ferror(fp));

    clearerr(fp);

    printf("After clearerr:\n");
    printf("feof(): %d, ferror(): %d\n", feof(fp), ferror(fp));

    fclose(fp);
    return 0;
}

此代码演示了在清除错误状态之前检查错误状态的重要性。它读取文件直到 EOF,然后在调用 clearerr 之前和之后显示 EOF 和错误标志的状态。这种做法有助于调试和理解文件流在操作过程中的确切状态。切勿无条件地清除错误,而应始终验证清除错误的必要性。

处理临时文件错误

此示例展示了如何在文件操作中处理临时错误。

temp_error.c
#include <stdio.h>
#include <errno.h>

int main() {
    FILE *fp = fopen("tempfile.txt", "r+");
    if (fp == NULL) {
        perror("Failed to open file");
        return 1;
    }

    // Simulate a temporary error (e.g., network hiccup)
    errno = EIO;  // Input/output error
    ferror(fp);   // Set the error indicator

    if (ferror(fp)) {
        printf("Temporary error occurred (errno: %d)\n", errno);
        clearerr(fp);
        errno = 0;  // Also reset errno
        
        // Try the operation again
        printf("Retrying operation...\n");
        if (fprintf(fp, "Retry data") < 0) {
            perror("Operation failed again");
        } else {
            printf("Retry succeeded\n");
        }
    }

    fclose(fp);
    return 0;
}

此示例通过手动设置 errno 来模拟临时的 I/O 错误(例如网络中断)。在用 ferror 检测到错误后,它会清除流错误(使用 clearerr)和全局 errno。然后重试该操作。这种模式对于处理实际应用程序中的瞬时错误非常有用,在这些错误中,立即重试可能会成功。

使用 clearerr 的最佳实践

来源

C clearerr 文档

本教程深入探讨了 clearerr 函数,展示了如何在 C 文件操作中有效管理错误状态。适当的错误处理可以使您的程序在处理文件时更加健壮和可靠。

作者

我的名字是 Jan Bodnar,我是一名敬业的程序员,对编码充满热情。自 2007 年以来,我通过 1400 多篇文章和 8 本电子书分享我的专业知识。凭借十多年的教学经验,我致力于使编程易于理解且引人入胜。

列表 C 标准库