【问题标题】:C read file(s) line-by-line into array of Strings and sortC逐行读取文件到字符串数组并排序
【发布时间】:2016-07-27 03:24:51
【问题描述】:

所以我想创建一个基本的 C 应用程序mysort,它获取文件列表,将每个文件逐行读取到缓冲区中,并按字母顺序对行进行排序。代码看起来像这样(加上参数解析等):

//How do I initialize an array of 1024byte-Strings with an unknown amount of fields?
char** lines; 
int lineNum = 0;

for(int num_files = j; num_files < argc; num_files++){ //iterate through all files
  FILE * filepointer ;
  char * line = NULL;
  size_t len = 0;
  ssize_t read;

  filepointer = fopen(argv[num_files], "r");    
  if (filepointer == NULL)
    exit(EXIT_FAILURE);

  //TODO: write each line into a new spot of the array, this try doesn't work!

  while ((read = getline(&line, &len, filepointer)) != -1) { 
    //the lines may be assumed to be a max of 1024 bytes
    lines[lineNum] = malloc(1024 * sizeof(char)); 
    //lines[lineNum] = line;
    strcpy(lines[lineNum], line);
    lineNum++;
  }

  fclose(fp);
  if (line)
    free(line);

  //These values might be wrong, but that isn't the issue I'm adressing
  //just for illustration
  qsort(lines , argc - 1, sizeof(char *), cmpstringp) 

  //do something with the sorted lines
}

因为我必须使用qsort(3),所以我需要生成一个char**,在某个时刻保存所有行。

有什么好的方法可以完成这样的任务?我是否需要自己的数据结构来动态存储多个相同的对象?

lines char** 数组在这里没有初始化,所以程序不工作。但是由于在程序开始时行数是完全未知的,它可能没有明确定义(除非你知道一个聪明的函数来解决这个问题)

到目前为止,我想出的唯一方法是定义我自己的动态数据结构(例如 LinkedList)或解析所有文件两次以确定将产生的行数。

在我看来,两者都不太优雅,但也许我只是不习惯 C 代码。

【问题讨论】:

  • for "lines[lineNum] = line;"你应该使用 strcpy 来代替
  • lines[lineNum] = malloc(...);lines[] 此时仍未初始化。
  • 您的数据结构会是一个更好的主意,但更快的解决方案是两次读取文件,第一次发现行数,第二次用于填充变量
  • 行只是一个指针数组,从某个大小开始,当你达到容量时重新分配/增长它
  • @BeyelerStudios 这有效地实现了一个 ArrayList。我只是想知道 C 是否提供了一些方便的方法来做我想做的事情。

标签: c arrays string file


【解决方案1】:

我认为解决问题的两种方法:

1) 浏览文件,计算换行字符的数量(并将其保存到 nl_count 中),然后您可以像这样分配行。

int nl_count = 0;
int c;

while ((c = fgetc(fp)) != EOF)
   if (c == '\n')
      nl_count++;
...
lines = malloc(nl_count * sizeof(char *));


这样,您将不得不在 cmpstringp 函数中涵盖一些特殊情况,因为您可能会得到一些仅包含 '\n' 的行。
编辑1。实际上,无论哪种情况,您都必须检查这种特殊情况。)
编辑2。你可以解决一个错误,因为最后一行不必以'\n'结尾。)

2) 为行设置一些基本大小,并在实际读取的行数达到此基本大小时重新分配更多空间。

#define BASE_SIZE 32
#define GROW_STEP 2

int size;

size = BASE_SIZE
lines = malloc(size * sizeof(char *));

lines_read = 0;
while ((read = getline(&line, &len, fp)) != -1) { 
   lines_read++;
   if (lines_read > size) {
       size *= GROW_STEP;
       lines = realloc (lines, size * sizeof (char *));
   }
   lines[lineNum] = strdup(line);
   lineNum++;
}

请注意,在最坏的情况下,您分配的空间将是实际需要的两倍。
另外,如果你使用 strdup(),你应该释放分配的内存。

...
for (i = 0; i < lines_read; i++)
    free(lines[i]);

【讨论】:

  • “计算换行符的个数”来确定文件中的行数在文件中的最后一个字符不是'\n' 时会导致off-by-1 错误。跨度>
  • getline() 是一个非常有用的功能。不幸的是,它不在标准 C 库中。
  • 另外,getline() 可以重用它的缓冲区,所以lines[lineNum] = line; 是不安全的——存储的指针对于多行可能是相同的,getline() 可以realloc() 它所以它可能也无效。如果要使用非标准函数,最好使用lines[lineNum] = strdup( line );
  • @chux 很好注意,完全忘记了那个 obob。 Andrew Henle 不知道 getline() 使用相同的缓冲区,在手册页中找不到它,我将编辑帖子以包含 obob 和这但第一件事。我刚刚读到 getline() 也不会停留在作为 line 一部分的空字符处,因此在 'getline(&line, &len, fp)' 调用之后,line 可能有多个 '\0' 字符。这不应该使 中的字符串函数无用吗,因为它们只需要一个 '\0',并且它必须是最后一个字符。 gnu.org/software/libc/manual/html_node/Line-Input.html
  • @Rorsch 在 C 中,从文件中读取字符并遇到空字符是一个问题,无论它是否是“最后一个”。顺便说一句:fgets() 也一样,在读取空字符后不会停止。
【解决方案2】:
//How do I initialize an array of 1024byte-Strings with an unknown amount of fields?

显然,你不知道。如果你初始化了某个东西,那么你就知道那个东西的所有细节了。

我想你是在问如何为未知数量的字符串指针保留内存,但同样,你没有。此外,请注意,对于您建议的 char * 数组,1024 字节的限制是不必要的;仅当您打算将数据构造为char 的二维数组时才有意义。读完一个字符串后,您知道它需要多少空间,例如,我观察到这段代码...

    //the lines may be assumed to be a max of 1024 bytes
    lines[lineNum] = malloc(1024 * sizeof(char)); 
    //lines[lineNum] = line;
    strcpy(lines[lineNum], line);

... 如果将其写为:

会更简单且没有固有的大小限制
    lines[linenum] = strdup(line);

事实上,如果您的行平均少于 1023 个字符,这也会占用更少的空间。

关于整个数组的空间,你可以做的是在你去的时候以增量的方式保留内存。这可能意味着最初malloc()ing 为多个字符串留出空间,realloc()ing 在需要时获得更多空间。这也可能意味着最初将字符串读取到单个字符串或固定大小的字符串数组的链表中,然后在知道有多少个字符串后构建您的整体数组。

链表替代方案暂时需要两倍的存储空间来存储字符串指针,但这并不算太糟糕,因为不需要复制字符串内容。相对于 malloc() / realloc() 方法的一些幼稚实现,这具有内存分配成本相对较低的优势。

因为重新分配通常需要将所有数据(在本例中为指针)从一个块复制到一个新的更大的块,所以您通常希望限制重新分配的次数。在像您这样的情况下,通常的策略是以几何方式而不是线性方式增加分配大小。也就是说,每次你发现你需要更多空间时,你就分配足够的新空间,比如说,你已经拥有的字符串的两倍。总成本与数据数量成线性关系。尽管如果您只需要 一点 空间,这似乎很浪费,但它仍然不需要比链表 + 转换为动态数组所需的更多空间。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-07-13
    • 2019-01-29
    • 1970-01-01
    • 1970-01-01
    • 2018-08-16
    • 1970-01-01
    • 1970-01-01
    • 2013-11-13
    相关资源
    最近更新 更多