将文件中的每一行读入数组

5
我正在阅读一个文件,想把每一行放入数组中的一个字符串。文件的长度是任意的,每一行的长度也是任意的(尽管假设它将小于100个字符)。
这是我的代码,但它不能编译。本质上,这是一个字符数组的数组,对吗?所以应该是char ** words = (** char) malloc(sizeof(*char));吗?
#include <stdio.h>
#include <stdlib.h>

int main(){


 int BUFSIZE = 32767;//max number of lines to read
 char** words = (**char)malloc(sizeof(*char));//gives error: expected expression before 'char'
 FILE *fp = fopen("coll.txt", "r");
 if (fp == 0){
        fprintf(stderr, "Error opening file");
        exit(1);
 }

int i = 0;
words[i] = malloc(BUFSIZE);
while(fscanf(fp, "%100s", words[i]) == 1)//no line will be longer than 100
{
        i++;
        words[i] = realloc(words, sizeof(char*)*i);
 }

 int j;
 for(j = 0; j < i; j++)
    printf("%s\n", words);

 return 0;
}

注意:我已经阅读了“从文件中读取并存储到数组中”但它没有回答我的问题。

你正在尝试查找字符指针的大小,因此应该是char*... - AurA
1
请注意,%100s将(a)跳过前导空格并在非空格字符后的空格处停止读取,并且(b)将超出大小为100的缓冲区一个字节,这可能很重要。您必须在转换规范中指定比数组大小少一个。 - Jonathan Leffler
2个回答

15

你的程序存在一些问题。realloc()语句使用不正确。我更喜欢使用fgets()来获取一行。这是我的解决方案。它还使用realloc()来增加缓冲区行的分配,因此你既不必预先知道行数,也不必进行两次文件读取(那样更快)。当你不知道需要提前分配多少内存时,这是一种常见的技巧。

#include <stdio.h>
#include <stdlib.h>

int main(void)

    {
    int lines_allocated = 128;
    int max_line_len = 100;

    /* Allocate lines of text */
    char **words = (char **)malloc(sizeof(char*)*lines_allocated);
    if (words==NULL)
        {
        fprintf(stderr,"Out of memory (1).\n");
        exit(1);
        }

    FILE *fp = fopen("coll.txt", "r");
    if (fp == NULL)
        {
        fprintf(stderr,"Error opening file.\n");
        exit(2);
        }

    int i;
    for (i=0;1;i++)
        {
        int j;

        /* Have we gone over our line allocation? */
        if (i >= lines_allocated)
            {
            int new_size;

            /* Double our allocation and re-allocate */
            new_size = lines_allocated*2;
            words = (char **)realloc(words,sizeof(char*)*new_size);
            if (words==NULL)
                {
                fprintf(stderr,"Out of memory.\n");
                exit(3);
                }
            lines_allocated = new_size;
            }
        /* Allocate space for the next line */
        words[i] = malloc(max_line_len);
        if (words[i]==NULL)
            {
            fprintf(stderr,"Out of memory (3).\n");
            exit(4);
            }
        if (fgets(words[i],max_line_len-1,fp)==NULL)
            break;

        /* Get rid of CR or LF at end of line */
        for (j=strlen(words[i])-1;j>=0 && (words[i][j]=='\n' || words[i][j]=='\r');j--)
            ;
        words[i][j+1]='\0';
        }
    /* Close file */
    fclose(fp);

    int j;
    for(j = 0; j < i; j++)
        printf("%s\n", words[j]);

    /* Good practice to free memory */
    for (;i>=0;i--)
        free(words[i]);
    free(words);
    return 0;
    }

1
+1. 我强烈建议将for循环的分号放在单独的一行上。如果将其追加到关闭括号后的同一行,很容易被误认为是打字错误或忽略掉。理论上,你可以在一行上有多个'\r'字符;这可能会有影响(但更可能不会)。 - Jonathan Leffler
@JonathanLeffler -- 我已经实现了您的建议。谢谢。 - willus
1
@willus,你是怎么决定 int lines_allocated 的值为128的? - Celeritas
@Celeritas--好问题。选择一个不太大的数字以便在最初分配内存时不会过度分配,但又不要选得太小导致realloc调用频繁。由于你只是在分配指针数组,因此可以从较大的值开始。2倍乘数也可以进行调整,一般会使用1.5倍乘数。这在某种程度上取决于你认为典型情况会是什么样子。如果你真的想优化这些值,可以进行内存使用和运行时间的研究,但其性能变化可能很小。 - willus
1
好答案!用for循环去除行末的\n和/或\r会多删除一个字符,将\0放在不是换行符的字符串的最后一个字符(从而截断该最后一个字符)。你应该将words[i][j]更改为words[i][j+1]。此外,使用fclose()关闭打开的文件是一个好习惯。 - Stefan van den Akker
@Neftas -- 很好的发现。谢谢你。我已经根据你的建议编辑了源代码。 - willus

1

您应该更改以下行:

char** words = (**char)malloc(sizeof(*char));

转化为:

char** words=(char **)malloc(sizeof(char *)*Max_Lines);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接