代码之家  ›  专栏  ›  技术社区  ›  kata

将文本文件的每一行存储到数组中

  •  1
  • kata  · 技术社区  · 10 年前

    我正在尝试将文本文件的每一行保存到一个数组中。 他们的方式是我这样做,到目前为止效果很好:

    char *lines[40];
    char line[50];
    int i = 0 ;
    char* eof ;
    while( (eof = fgets(line, 50, in)) != NULL )
    {
        lines[i] = strdup(eof); /*Fills the array with line of the txt file one by one*/
        i++;
    }
    

    我的文本文件有40行,我使用for循环访问这些行

    for( j = 0; j <= 39 ; j++)
    { /*Do something to each line*/}.
    

    到现在为止,一直都还不错。我的问题是我定义了数组的大小 线 对于有40行的文本文件。我试着数数线条,然后确定尺寸,但我得到了 分段故障 .

    我的方法:

    int count=1 ; char c ;
    for (c = getc(in); c != EOF; c = getc(in))
        if (c == '\n') // Increment count if this character is newline
            count = count + 1;
    printf("\nNUMBER OF LINES = %d \n",count); 
    
    char* lines[count];
    

    有什么想法吗?

    2 回复  |  直到 10 年前
        1
  •  1
  •   ryyker    10 年前

    顺便说一句,我测试了上面显示的代码,以获得一个包含1000多行的文件中的行数(通过计算换行符),其中一些行长4000个字符。问题不存在。 这个 seg断层 因此可能是由于您为每个行缓冲区分配内存的方式。您可能试图将一个长行写入一个短缓冲区。(也许我在你的帖子中错过了它,但找不到你的地址行长度?)

    在为文件中存储字符串分配内存时,有两件事很有用:行数和文件中的最大行长度。这些可用于创建 char 数组。

    您可以通过循环获得行数和最长行数 fgets(...) :(你的主题的变化,本质上让 fgets 查找换行符)

    int countLines(FILE *fp, int *longest)
    {
        int i=0;
        int max = 0;
        char line[4095];  // max for C99 strings
        *longest = max;
        while(fgets(line, 4095, fp))
        {
            max = strlen(line); 
            if(max > *longest) *longest = max;//record longest
            i++;//track line count
        }
        return i;
    }
    int main(void)
    {
        int longest;
        char **strArr = {0};
        FILE *fp = fopen("C:\\dev\\play\\text.txt", "r");
        if(fp)
        {
            int count = countLines(fp, &longest);
            printf("%d", count);
            GetKey();
        }
        // use count and longest to create memory
        strArr = create2D(strArr, count, longest);
        if(strArr)
        {
           //use strArr ...
           //free strArr
           free2D(strArr, lines);
        }
        ......and so on
        return 0;   
    }
    
    char ** create2D(char **a, int lines, int longest)
    {
        int i;
        a = malloc(lines*sizeof(char *));
        if(!a) return NULL;
        {
            for(i=0;i<lines;i++)
            {
                a[i] = malloc(longest+1);
                if(!a[i]) return NULL;
            }
        }
        return a;
    }
    
    void free2D(char **a, int lines)
    {
        int i;
        for(i=0;i<lines;i++)
        {
            if(a[i]) free(a[i]);
        }
        if(a) free(a);
    }
    
        2
  •  0
  •   David C. Rankin    7 年前

    有很多方法可以解决这个问题。声明静态2D数组或char(例如。 char lines[40][50] = {{""}}; )或声明 指向char[50]类型数组的指针 ,这可能是最容易进行动态分配的。使用这种方法,您只需要一次分配。使用常量 MAXL = 40 MAXC = 50 ,您只需要:

    char (*lines)[MAXC] = NULL;
    ...
    lines = malloc (MAXL * sizeof *lines);
    

    阅读每一行 fgets 是一项简单的任务:

    while (i < MAXL && fgets (lines[i], MAXC, fp)) {...
    

    当你完成后,你需要做的就是 free (lines); 把这些碎片放在一起,你可以这样做:

    #include <stdio.h>
    #include <stdlib.h>
    
    enum { MAXL = 40, MAXC = 50 };
    
    int main (int argc, char **argv) {
    
        char (*lines)[MAXC] = NULL; /* pointer to array of type char [MAXC] */
        int i, n = 0;
        FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
    
        if (!fp) {  /* valdiate file open for reading */
            fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
            return 1;
        }
    
        if (!(lines = malloc (MAXL * sizeof *lines))) { /* allocate MAXL arrays */
            fprintf (stderr, "error: virtual memory exhausted 'lines'.\n");
            return 1;
        }
    
        while (n < MAXL && fgets (lines[n], MAXC, fp)) { /* read each line */
            char *p = lines[n];                  /* assign pointer */
            for (; *p && *p != '\n'; p++) {}     /* find 1st '\n'  */
            *p = 0, n++;                         /* nul-termiante  */
        }
        if (fp != stdin) fclose (fp);   /* close file if not stdin */
    
        /* print lines */
        for (i = 0; i < n; i++) printf (" line[%2d] : '%s'\n", i + 1, lines[i]);
    
        free (lines);   /* free allocated memory */
    
        return 0;
    }
    

    注意: 您还需要检查整行是否被 fgets公司 每次。(假设文件中有一行超过38个字符)。您可以通过检查 *p '\n' 在使用 nul终止 性格(例如。 if (*p != '\n') { int c; while ((c = getchar()) != '\n' && c != EOF) {} } ). 确保下次阅读 fgets公司 将从下一行开始,而不是当前行中的剩余字符。

    要包括支票,您可以执行以下类似操作( 注意: 我将读取循环计数器从 i n 无需分配 n = i; 跟随读取循环)。

        while (n < MAXL && fgets (lines[n], MAXC, fp)) { /* read each line */
            char *p = lines[n];                 /* assign pointer  */
            for (; *p && *p != '\n'; p++) {}    /* find 1st '\n'   */
            if (*p != '\n') {                   /* check line read */
                int c;  /* discard remainder of line with getchar  */
                while ((c = fgetc (fp)) != '\n' && c != EOF) {}
            }
            *p = 0, n++;                        /* nul-termiante   */
        }
    

    是否丢弃或保留超出数组长度的剩余行取决于您。然而,经常检查是一个好主意。(以下示例输入中的文本行数限制为17个字符,因此不可能有长行,但通常不能保证行长。

    示例输入

    $ cat dat/40lines.txt
    line of text -  1
    line of text -  2
    line of text -  3
    line of text -  4
    line of text -  5
    line of text -  6
    ...
    line of text - 38
    line of text - 39
    line of text - 40
    

    示例使用/输出

    $ ./bin/fgets_ptr2array <dat/40lines.txt
     line[ 1] : 'line of text -  1'
     line[ 2] : 'line of text -  2'
     line[ 3] : 'line of text -  3'
     line[ 4] : 'line of text -  4'
     line[ 5] : 'line of text -  5'
     line[ 6] : 'line of text -  6'
    ...
     line[38] : 'line of text - 38'
     line[39] : 'line of text - 39'
     line[40] : 'line of text - 40'
    

    现在包括一个长度签入代码,并在输入中添加一个长行,例如:

    $ cat dat/40lines+long.txt
    line of text -  1
    line of text -  2
    line of text -  3 + 123456789 123456789 123456789 123456789 65->|
    line of text -  4
    ...
    

    重新运行该程序,您可以确认您现在已经保护了文件中的长行,从而妨碍了从文件中连续读取行。


    动态重新分配 lines

    如果文件中的行数未知,并且达到了初始分配 40 在里面 线 ,那么您需要做的就是继续阅读其他行 realloc 存储 线 例如:

        int i, n = 0, maxl = MAXL;
        ...
        while (fgets (lines[n], MAXC, fp)) {     /* read each line */
            char *p = lines[n];                  /* assign pointer */
            for (; *p && *p != '\n'; p++) {}     /* find 1st '\n'  */
            *p = 0;                              /* nul-termiante  */
            if (++n == maxl) { /* if limit reached, realloc lines  */
                void *tmp = realloc (lines, 2 * maxl * sizeof *lines);
                if (!tmp) {     /* validate realloc succeeded */
                    fprintf (stderr, "error: realloc - virtual memory exhausted.\n");
                    break;      /* on failure, exit with existing data */
                }
                lines = tmp;    /* assign reallocated block to lines */
                maxl *= 2;      /* update maxl to reflect new size */
            }
        }
    

    现在,无论文件中有多少行,您只需继续重新分配 线 直到整个文件被读取,或者内存不足。(注意:当前代码为重新分配两倍的当前内存 线 每次重新分配时。您可以随意添加任意数量的内容。例如,您可以分配 maxl + 40 简单地分配 40 每次多行。

    编辑以响应评论查询

    如果您确实想使用固定数量的 线 您必须分配固定数量的额外 线 (增加次数 sizeof *lines ),您不能简单地添加 40 字节,例如。

            void *tmp = realloc (lines, (maxl + 40) * sizeof *lines);
                if (!tmp) {     /* validate realloc succeeded */
                    fprintf (stderr, "error: realloc - virtual memory exhausted.\n");
                    break;      /* on failure, exit with existing data */
                }
                lines = tmp;    /* assign reallocated block to lines */
                maxl += 40;     /* update maxl to reflect new size */
            }
    

    回忆起 线 是一个 指向数组的指针 属于 char[50] ,因此对于要分配的每一行,必须为50个字符(例如。 *行大小 ),因此固定增加40行将是 realloc (lines, (maxl + 40) * sizeof *lines); ,则必须准确更新已分配的最大行数( maxl )以反映 40 线路,例如。 maxl += 40; .

    示例输入

    $ cat dat/80lines.txt
    line of text -  1
    line of text -  2
    ...
    line of text - 79
    line of text - 80
    

    示例使用/输出

    $ ./bin/fgets_ptr2array_realloc <dat/80lines.txt
     line[ 1] : 'line of text -  1'
     line[ 2] : 'line of text -  2'
    ...
     line[79] : 'line of text - 79'
     line[80] : 'line of text - 80'
    

    仔细看看,如果你有任何问题,请告诉我。