我正在尝试将文本文件的每一行保存到数组中。 他们这样做的方式到目前为止工作得很好是这样的:
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];
有什么想法吗?
答案 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 = 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个字符的长行)。您可以通过在使用 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
仔细看看,如果您有任何问题,请告诉我。