【问题标题】:Reading text file into an array of lines in C将文本文件读入C中的行数组
【发布时间】:2010-12-20 11:51:52
【问题描述】:

使用 C 我想以这样一种方式读取文本文件的内容,以便在说完所有内容后获得一个字符串数组,其中第 n 个字符串表示文本文件的第 n 行。文件的行数可以任意长。

完成此任务的优雅方式是什么?我知道将文本文件直接读取到单个大小合适的缓冲区中的一些巧妙技巧,但是将其分成几行会使它变得更加棘手(至少据我所知)。

非常感谢!

【问题讨论】:

    标签: c arrays file text


    【解决方案1】:

    你可以这样用

    #include <stdlib.h> /* exit, malloc, realloc, free */
    #include <stdio.h>  /* fopen, fgetc, fputs, fwrite */
    
    struct line_reader {
        /* All members are private. */
        FILE    *f;
        char    *buf;
        size_t   siz;
    };
    
    /*
     * Initializes a line reader _lr_ for the stream _f_.
     */
    void
    lr_init(struct line_reader *lr, FILE *f)
    {
        lr->f = f;
        lr->buf = NULL;
        lr->siz = 0;
    }
    
    /*
     * Reads the next line. If successful, returns a pointer to the line,
     * and sets *len to the number of characters, at least 1. The result is
     * _not_ a C string; it has no terminating '\0'. The returned pointer
     * remains valid until the next call to next_line() or lr_free() with
     * the same _lr_.
     *
     * next_line() returns NULL at end of file, or if there is an error (on
     * the stream, or with memory allocation).
     */
    char *
    next_line(struct line_reader *lr, size_t *len)
    {
        size_t newsiz;
        int c;
        char *newbuf;
    
        *len = 0;           /* Start with empty line. */
        for (;;) {
            c = fgetc(lr->f);   /* Read next character. */
            if (ferror(lr->f))
                return NULL;
    
            if (c == EOF) {
                /*
                 * End of file is also end of last line,
            `    * unless this last line would be empty.
                 */
                if (*len == 0)
                    return NULL;
                else
                    return lr->buf;
            } else {
                /* Append c to the buffer. */
                if (*len == lr->siz) {
                    /* Need a bigger buffer! */
                    newsiz = lr->siz + 4096;
                    newbuf = realloc(lr->buf, newsiz);
                    if (newbuf == NULL)
                        return NULL;
                    lr->buf = newbuf;
                    lr->siz = newsiz;
                }
                lr->buf[(*len)++] = c;
    
                /* '\n' is end of line. */
                if (c == '\n')
                    return lr->buf;
            }
        }
    }
    
    /*
     * Frees internal memory used by _lr_.
     */
    void
    lr_free(struct line_reader *lr)
    {
        free(lr->buf);
        lr->buf = NULL;
        lr->siz = 0;
    }
    
    /*
     * Read a file line by line.
     * http://rosettacode.org/wiki/Read_a_file_line_by_line
     */
    int
    main()
    {
        struct line_reader lr;
        FILE *f;
        size_t len;
        char *line;
    
        f = fopen("foobar.txt", "r");
        if (f == NULL) {
            perror("foobar.txt");
            exit(1);
        }
    
        /*
         * This loop reads each line.
         * Remember that line is not a C string.
         * There is no terminating '\0'.
         */
        lr_init(&lr, f);
        while (line = next_line(&lr, &len)) {
            /*
             * Do something with line.
             */
            fputs("LINE: ", stdout);
            fwrite(line, len, 1, stdout);
        }
        if (!feof(f)) {
            perror("next_line");
            exit(1);
        }
        lr_free(&lr);
    
        return 0;
    }
    

    【讨论】:

      【解决方案2】:

      可以读取文件中的行数(循环 fgets),然后创建一个二维数组,第一维是行数+1。然后,只需将文件重新读入数组即可。

      不过,您需要定义元素的长度。或者,计算最长的行大小。

      示例代码:

      inFile = fopen(FILENAME, "r");
      lineCount = 0;
      while(inputError != EOF) {
          inputError = fscanf(inFile, "%s\n", word);
          lineCount++;
      }
      fclose(inFile);
        // Above iterates lineCount++ after the EOF to allow for an array
        // that matches the line numbers
      
      char names[lineCount][MAX_LINE];
      
      fopen(FILENAME, "r");
      for(i = 1; i < lineCount; i++)
          fscanf(inFile, "%s", names[i]);
      fclose(inFile);
      

      【讨论】:

        【解决方案3】:

        将其分解为行意味着解析文本并将所有 EOL(EOL 我的意思是 \n 和 \r)字符替换为 0。 通过这种方式,您实际上可以重用缓冲区并将每行的开头存储到单独的 char * 数组中(只需执行 2 遍)。

        通过这种方式,您可以对整个文件大小进行一次读取+2 次解析,这可能会提高性能。

        【讨论】:

        • 这绝对是最好的方法,尽管它可能需要对整个文件进行多次遍历。您需要计算行数(以便您可以分配正确大小的数组),将 \n 替换为 0,然后将每行的开头分配到数组中的正确位置。当然,您可以分两次执行此操作。
        • 一个非常好的主意。我要试一试。
        • +1 不计算从文件到缓冲区的初始复制,您可以使用realloc()strtok() 进行单次传递。
        • 同意这需要 2 遍。至少我现在不知道通过一次通行证的方法。相应地更新了帖子。
        • 为什么需要两次通过?使用malloc() 为阵列分配您认为需要的空间。从缓冲区开始。对于每个 '\n',替换为 0,并将下一个字符的地址放入数组中。跟踪数组大小;如果它会溢出,realloc() 它。
        【解决方案4】:

        如果您有一种将整个文件读入内存的好方法,那么您就快到了。完成后,您可以扫描文件两次。一次计算行数,一次设置行指针并将“\n”和(如果文件以 Windows 二进制模式读取,则可能是“\r”)替换为“\0”。在扫描之间分配一个指针数组,现在您知道需要多少。

        【讨论】:

          【解决方案5】:

          也许链接列表是最好的方法? 编译器不喜欢有一个不知道有多大的数组。使用链接列表,您可以拥有一个非常大的文本文件,而不必担心为数组分配足够的内存。

          很遗憾,我还没有学会如何做链表,但也许其他人可以帮助你。

          【讨论】:

          • 任意大小是链表的一个吸引人的特性,但要获得它,您需要放弃随机访问。例如,如果不先获得第 0-4 行,就无法获得第 5 行。但是将链表构建为中间结构是个好主意,然后您可以轻松构建数组。
          • 不幸的是,链接列表在这种情况下不是很合适,因为我遗漏了一些细节(简而言之,我需要随机访问)。当然,我可以将所有内容读入一个链表,然后将内容复制到一个数组中,但我希望有一种更优雅的方法。
          【解决方案6】:

          对于 C(相对于 C++),您可能最终会使用 fgets()。但是,您可能会因为任意长度的线条而遇到问题。

          【讨论】:

            猜你喜欢
            • 2020-01-20
            • 2010-09-29
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-08-18
            • 1970-01-01
            相关资源
            最近更新 更多