【问题标题】:Read line from file without knowing the line length在不知道行长的情况下从文件中读取行
【发布时间】:2010-03-28 09:29:30
【问题描述】:

我想逐行读取文件,而不知道之前的行长。到目前为止,这是我得到的:

int ch = getc(file);
int length = 0;
char buffer[4095];

while (ch != '\n' && ch != EOF) {
    ch = getc(file);
    buffer[length] = ch;
    length++;
}

printf("Line length: %d characters.", length);

char newbuffer[length + 1];

for (int i = 0; i < length; i++)
    newbuffer[i] = buffer[i];

newbuffer[length] = '\0';    // newbuffer now contains the line.

我现在可以计算出行长,但仅限于短于 4095 个字符的行,而且这两个 char 数组似乎是一种尴尬的完成任务的方式。 有没有更好的方法来做到这一点(我已经使用了 fgets() 但被告知这不是最好的方法)?

--Ry

【问题讨论】:

    标签: c file-io


    【解决方案1】:

    您可以从您选择的一些合适的大小开始,然后如果您需要更多空间,请在中途使用realloc

    int CUR_MAX = 4095;
    char *buffer = (char*) malloc(sizeof(char) * CUR_MAX); // allocate buffer.
    int length = 0;
    
    while ( (ch != '\n') && (ch != EOF) ) {
        if(length ==CUR_MAX) { // time to expand ?
          CUR_MAX *= 2; // expand to double the current size of anything similar.
          buffer = realloc(buffer, CUR_MAX); // re allocate memory.
        }
        ch = getc(file); // read from stream.
        buffer[length] = ch; // stuff in buffer.
        length++;
    }
    .
    .
    free(buffer);
    

    您必须在调用mallocrealloc 后检查分配错误。

    【讨论】:

    • 就像一个注释一样,逐个字符的阅读速度非常慢。你应该大块地阅读它(4-16k)。
    • @Blindy:标准库 I/O 会进行缓冲,所以这并不比分块读取慢很多。
    • 不将计数重置为 0 导致缓冲区溢出?
    • 一如既往,请don't cast the result of malloc()
    • 为什么在扩大内存大小后将计数重置为0?以前的记忆还在吗?
    【解决方案2】:

    您可能想查看Chuck B. Falconer's public domain ggets library。如果您在使用 glibc 的系统上,您可能有一个(非标准)getline 函数可供您使用。

    【讨论】:

    • 不错!我相信我可以相信大多数类 UNIX 系统都安装了 glibc,所以这绝对是一种很好的逐行阅读方式。
    • 此外,getline 已包含在最新的 POSIX 标准中,因此它现在 unix 上的标准。但是,仍然不能保证它包含在 c per se 中。
    【解决方案3】:

    你已经接近了。基本上你想读取数据块并检查它们是否有\n 字符。如果你找到一个,很好,你已经结束了。如果不这样做,则必须增加缓冲区(即分配一个两倍于第一个缓冲区大小的新缓冲区,并将第一个缓冲区中的数据复制到新缓冲区中,然后删除旧缓冲区并将新缓冲区重命名为旧的 -- 或者只是 realloc 如果你在 C) 然后再读一些直到你找到一个结局。

    一旦你有了结尾,从缓冲区开头到\n 字符的文本就是你的行。将其复制到缓冲区或就地处理,由您决定。

    准备好下一行后,您可以将输入的“其余部分”复制到当前行(基本上是左移)并用输入中的数据填充缓冲区的其余部分。然后你再去一次,直到你的数据用完。

    这当然可以优化,例如使用循环缓冲区,但这对于任何合理的 io-bound 算法来说应该绰绰有余。

    【讨论】:

      【解决方案4】:

      这就是我为标准输入所做的,如果你像 readLine(NULL, 0) 这样调用它,该函数会为你分配一个大小为 1024 的缓冲区,并让它以 1024 的步长增长。如果你用 readLine(NULL, 10) 调用函数你得到一个步长为 10 的缓冲区。如果你有一个缓冲区,你可以提供它的大小。

      #include <stdio.h>
      #include <stdlib.h>
      #include <assert.h>
      #include <string.h>
      
      char *readLine(char **line, size_t *length)
      {
          assert(line != NULL);
          assert(length != NULL);
      
          size_t count = 0;
      
          *length = *length > 0 ? *length : 1024;
      
          if (!*line)
          {
              *line = calloc(*length, sizeof(**line));
              if (!*line)
              {
                  return NULL;
              }
          }
          else
          {
              memset(*line, 0, *length);
          }
      
          for (int ch = getc(stdin); ch != '\n' && ch != EOF; ch = getc(stdin))
          {
              if (count == *length)
              {
                  *length += 2;
                  *line = realloc(*line, *length);
                  if (!*line)
                  {
                      return NULL;
                  }
              }
      
              (*line)[count] = (char)ch;
      
              ++count;
          }
      
          return *line;
      }
      

      【讨论】:

        【解决方案5】:

        考虑 scanf '%m' 格式转换修饰符 (POSIX)

        char *arr = NULL ;
            // Read unlimited string, terminated with newline. Similar to dynamic size fgets.
        if ( fscanf(stdin, "%m[^\n]", &arr) == 1 ) {
           // Do something with arr
           free(arr) ;
        } ;
        

        引用 scanf 手册页:

        可选的“m”字符。 这与字符串转换一起使用 (%s, %c, %[),并解除调用者的 需要分配一个相应的缓冲区来保存输入:相反,scanf() 分配了一个足够的缓冲区 大小,并将此缓冲区的地址分配给相应的指针参数,该参数应该是指向 一个 char * 变量(该变量不需要在调用前初始化)。当不再需要此缓冲区时,调用者应随后释放(3)此缓冲区

        【讨论】:

          猜你喜欢
          • 2017-01-12
          • 1970-01-01
          • 1970-01-01
          • 2016-03-03
          • 1970-01-01
          • 1970-01-01
          • 2023-03-10
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多