在C中逐行浏览文本文件

Dan*_*ury 54 c scanf getline

我一直在为我的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;
}
Run Code Online (Sandbox Code Playgroud)

现在我用sscanf方法得到一个seg错误,我不知道为什么.我是一名C总裁,只是想知道是否有一些我失踪的大事.谢谢

APr*_*mer 126

这么几行中存在很多问题.我可能会忘记一些:

  • 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;
}
Run Code Online (Sandbox Code Playgroud)

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

Abr*_*as2 7

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

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

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


Bas*_*tch 6

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


Ale*_*lds 5

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

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

注意getc()返回一个int,这样我们就可以测试是否相等EOF.

其次,我们定义一个line[BUFFER_MAX_LENGTH]类型数组char,以便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;
}
Run Code Online (Sandbox Code Playgroud)

如果你必须使用a 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;
}
Run Code Online (Sandbox Code Playgroud)

  • 好的,我修复了循环.谢谢你的指针.我每天都学到新东西. (2认同)