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

时间:2016-04-22 19:24:59

标签: c arrays file line

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

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 个答案:

答案 0 :(得分:1)

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

分配用于在文件中存储字符串的内存时,有两件事是行数和文件中的最大行长度。这些可用于创建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);
}

答案 1 :(得分:0)

有很多方法可以解决这个问题。声明静态2D数组或char(例如char lines[40][50] = {{""}};)或声明指向char [50] 类型数组的指针,这可能是动态分配最简单的。使用这种方法,您只需要一次分配。使用常量MAXL = 40MAXC = 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个字符的长行)。您可以通过在使用 nul-terminatedating 字符覆盖之前检查*p是否为'\n'来执行此操作。 (例如if (*p != '\n') { int c; while ((c = getchar()) != '\n' && c != EOF) {} })。这确保了fgets的下一次读取将从下一行开始,而不是当前行中的剩余字符。

要包括检查,您可以执行类似以下操作(注意:我将读取循环计数器从i更改为n,以消除分配{的需要读取循环之后的{1}}。

n = i;

您是否丢弃或保留超出阵列长度的剩余行数取决于您。但是,始终检查是个好主意。 (下面我输入的示例中的文本行限制为17个字符,因此不存在长行,但通常无法保证行长。

示例输入

    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   */
    }

示例使用/输出

$ 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,那么您需要做的就是继续阅读其他行lines存储为realloc。例如:

lines

现在,文件中有多少行并不重要,您只需重新分配 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 */ } } ,直到读取整个文件,或者内存不足。 (注意:当前代码在每次重新分配时为lines重新分配当前内存的两倍。您可以根据需要随意添加。例如,您可以分配lines来简单地分配{ {1}}每次更多行。

编辑以回复评论查询

如果您确实希望使用maxl + 40的数量固定增加,而不是按某种因素进行缩放,则必须分配固定数量的额外40(增加时间lines }),你不能简单地添加lines个字节,例如

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 */ } 指向数组的指针,因此对于要分配的每个额外行,必须为50-char分配存储空间(例如lines),因此固定增加40行将为char[50],然后您必须准确更新最大行分配计数(sizeof *lines)以反映{{1}的增加线条,例如realloc (lines, (maxl + 40) * sizeof *lines);

示例输入

maxl

示例使用/输出

40

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