在 C 中逐行浏览文本文件

Posted

技术标签:

【中文标题】在 C 中逐行浏览文本文件【英文标题】:Going through a text file line by line in C 【发布时间】:2012-03-01 15:24:32 【问题描述】:

我一直在为我的 CIS 课程做一个小练习,并且对 C 用于读取文件的方法感到非常困惑。我真正需要做的就是逐行读取文件,并使用从每一行收集的信息进行一些操作。我尝试使用 getline 方法和其他没有运气的方法。 我的代码目前如下:

int main(char *argc, char* argv[])
      const char *filename = argv[0];
      FILE *file = fopen(filename, "r");
      char *line = NULL;

      while(!feof(file))
        sscanf(line, filename, "%s");
        printf("%s\n", line);
      
    return 1;

现在我遇到了 sscanf 方法的 seg 错误,我不知道为什么。我是一个总 C 菜鸟,只是想知道我是否遗漏了一些重要的事情。 谢谢

【问题讨论】:

这段代码甚至不应该编译。 sscanf(line, filename, "%s"); 应该是 sscanf(line, file, "%s"); 请注意while (!feof(file)) is always wrong。 C read file line by line的可能重复 【参考方案1】:

这么几行就这么多问题。我可能忘记了一些:

argv[0]是程序名,不是第一个参数; 如果你想读入一个变量,你必须分配它的内存 一个永远不会在 feof 上循环,一个在 IO 函数上循环直到它失败,然后 feof 用于确定失败的原因, sscanf是用来解析一行的,如果你想解析一个文件,使用fscanf, “%s”将作为 ?scanf 系列的格式在第一个空格处停止 读取一行,标准函数是fgets, 从 main 返回 1 表示失败

所以

#include <stdio.h>

int main(int argc, char* argv[])

    char const* const fileName = argv[1]; /* should check that argc > 1 */
    FILE* file = fopen(fileName, "r"); /* should check the result */
    char line[256];

    while (fgets(line, sizeof(line), file)) 
        /* note that fgets don't strip the terminating \n, checking its
           presence would allow to handle lines longer that sizeof(line) */
        printf("%s", line); 
    
    /* may check feof here to make a difference between eof and io failure -- network
       timeout for instance */

    fclose(file);

    return 0;

【讨论】:

在返回之前不要忘记fclose(file) fclose(file) 实际上没有必要,因为它发生在main 中,它会自动关闭所有打开的文件缓冲区。 @Leandros 安全总比抱歉好! 对于初学者来说还是不错的,因为有时即使在 main 的末尾也是必要的。 FILE* 对象在 C 中缓冲,因此如果正在将数据写入文件并且未调用 fclose,则某些数据可能不会被刷新。 嗨,@alecRN:你确定吗? AFAIK,当程序通过调用 exit 终止时,流上的缓冲输出会自动刷新(请参阅:gnu.org/software/libc/manual/html_node/Flushing-Buffers.html),并且操作系统将决定何时刷新(可以调用 fsync)。在执行结束时有一个对 exit_group 的隐式调用,您可以使用 strace 和 nm 看到它。我想它不是由 gcc 添加的,因为没有这样的符号,可能是运行时添加的。甚至 _exit 也会关闭打开的文件描述符。无论如何,我同意你的观点,明确关闭打开的文件是一个好习惯 /Ángel【参考方案2】:

要从文件中读取一行,您应该使用fgets 函数:它从指定文件中读取一个字符串,直到换行符或EOF

在您的代码中使用sscanf 根本不起作用,因为您使用filename 作为格式字符串,用于从line 读取到常量字符串文字%s

SEGV的原因是你写入了line指向的未分配内存。

【讨论】:

【参考方案3】:

除了其他答案之外,在最近的 C 库(符合 Posix 2008)上,您可以使用getline。请参阅this answer(相关问题)。

【讨论】:

【参考方案4】:

假设您正在处理一些其他分隔符,例如 \t 制表符,而不是 \n 换行符。

一种更通用的分隔符方法是使用getc(),它一次抓取一个字符。

请注意,getc() 返回一个int,以便我们可以测试与EOF 是否相等。

其次,我们定义了一个char 类型的数组line[BUFFER_MAX_LENGTH],以便在堆栈上最多存储BUFFER_MAX_LENGTH-1 个字符(我们必须将最后一个字符保存为\0 终止符)。

使用数组避免了使用mallocfree 在堆上创建正确长度的字符指针的需要。

#define BUFFER_MAX_LENGTH 1024

int main(int argc, char* argv[])

    FILE *file = NULL;
    char line[BUFFER_MAX_LENGTH];
    int tempChar;
    unsigned int tempCharIdx = 0U;

    if (argc == 2)
         file = fopen(argv[1], "r");
    else 
         fprintf(stderr, "error: wrong number of arguments\n"
                         "usage: %s textfile\n", argv[0]);
         return EXIT_FAILURE;
    

    if (!file) 
         fprintf(stderr, "error: could not open textfile: %s\n", argv[1]);
         return EXIT_FAILURE;
    

    /* get a character from the file pointer */
    while(tempChar = fgetc(file))
    
        /* avoid buffer overflow error */
        if (tempCharIdx == BUFFER_MAX_LENGTH) 
            fprintf(stderr, "error: line is too long. increase BUFFER_MAX_LENGTH.\n");
            return EXIT_FAILURE;
        

        /* test character value */
        if (tempChar == EOF) 
            line[tempCharIdx] = '\0';
            fprintf(stdout, "%s\n", line);
            break;
        
        else if (tempChar == '\n') 
            line[tempCharIdx] = '\0';
            tempCharIdx = 0U;
            fprintf(stdout, "%s\n", line);
            continue;
        
        else
            line[tempCharIdx++] = (char)tempChar;
    

    return EXIT_SUCCESS;

如果你必须使用char *,那么你仍然可以使用这个代码,但是你strdup()line[]数组,一旦它被填满了一行的输入。完成后必须free这个重复的字符串,否则会出现内存泄漏:

#define BUFFER_MAX_LENGTH 1024

int main(int argc, char* argv[])

    FILE *file = NULL;
    char line[BUFFER_MAX_LENGTH];
    int tempChar;
    unsigned int tempCharIdx = 0U;
    char *dynamicLine = NULL;

    if (argc == 2)
         file = fopen(argv[1], "r");
    else 
         fprintf(stderr, "error: wrong number of arguments\n"
                         "usage: %s textfile\n", argv[0]);
         return EXIT_FAILURE;
    

    if (!file) 
         fprintf(stderr, "error: could not open textfile: %s\n", argv[1]);
         return EXIT_FAILURE;
    

    while(tempChar = fgetc(file))
    
        /* avoid buffer overflow error */
        if (tempCharIdx == BUFFER_MAX_LENGTH) 
            fprintf(stderr, "error: line is too long. increase BUFFER_MAX_LENGTH.\n");
            return EXIT_FAILURE;
        

        /* test character value */
        if (tempChar == EOF) 
            line[tempCharIdx] = '\0';
            dynamicLine = strdup(line);
            fprintf(stdout, "%s\n", dynamicLine);
            free(dynamicLine);
            dynamicLine = NULL;
            break;
        
        else if (tempChar == '\n') 
            line[tempCharIdx] = '\0';
            tempCharIdx = 0U;
            dynamicLine = strdup(line);
            fprintf(stdout, "%s\n", dynamicLine);
            free(dynamicLine);
            dynamicLine = NULL;
            continue;
        
        else
            line[tempCharIdx++] = (char)tempChar;
    

    return EXIT_SUCCESS;

【讨论】:

我会对任何while(!feof(file)) 投反对票,即使是在不可损坏的蓝月亮出现的情况下(请注意,这里可能永远不会是真的,有一个休息时间可以离开在这种情况下,循环 while (true) 也可以。)有太多人认为这是正确的习惯用法。 我不知道这是个问题。老实说,我很想了解更多有关这方面的信息。这种用法有什么问题? 出现了很多问题,例如***.com/questions/5431941/…。 好的,我修复了循环。感谢您的指点。我每天都能学到新东西。

以上是关于在 C 中逐行浏览文本文件的主要内容,如果未能解决你的问题,请参考以下文章

在 Java 中逐行处理文本文件和连字符字符串

在 Swift 中逐行读取文本文件?

在 VBA 中逐行读取/解析文本文件

在Swift中逐行读取文本文件?

如何在文本文件中逐行替换特定行?

在 Fortran 中逐行读取逗号分隔的文本文件