fgets没有读取C中的完整行
Posted
技术标签:
【中文标题】fgets没有读取C中的完整行【英文标题】:fgets not reading complete line in C 【发布时间】:2015-02-12 08:44:26 【问题描述】:我有一个文件data.csv
,其中包含float
类型数据:
0.22,0.33,0.44
0.222,0.333,0.444
我需要将这个文件读入一个二维动态数组。但我无法阅读fgets
的完整行。不知道为什么?
这是我在 Ubuntu 上使用的 C 代码:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char *argv[])
FILE *fp;
float **data;
int i,j,rows=2,cols=3;
char * token;
fp=fopen("data.csv","r");
if(fp==NULL)
fprintf(stderr,"Can't open input file");
exit(1);
data= malloc(rows * sizeof(float*));
char *rowbuffer=malloc( cols * ( sizeof(float)+sizeof(char) ) );
i=0;
while(fgets(rowbuffer,sizeof(rowbuffer),fp) !=NULL)
data[i] = malloc(cols * sizeof(float));
j=0;
printf("\n %s",rowbuffer);
for (token = strtok(rowbuffer,","); token != NULL; token = strtok(NULL, ","))
data[i][j++] = atof(token);
/*printf("%s",token);*/
i++;
free(rowbuffer);
for(i = 0; i < rows; i++)
free(data[i]);
free(data);
fclose(fp);
输出如下:
0.22,0.
33,0.44
0.222,0
��
444
`./test' 中的错误:双重释放或损坏(输出):0x0000000000adf270
中止(核心转储)
谁能告诉我为什么会出现这个错误? :( 或者有没有更好的方法来读取这种数据文件?
【问题讨论】:
sizeof(rowbuffer) == sizeof( char * )
... 这可能是 4 或 8,具体取决于硬件。由于您假设它是分配缓冲区的大小,因此您的假设是错误的。
How to find the 'sizeof'(a pointer pointing to an array)? 的可能重复项
还有缩进。空格是免费的。 ;-)
【参考方案1】:
这里有一个问题:
char *rowbuffer=malloc( cols * ( sizeof(float)+sizeof(char) ) );
sizeof(float)
是浮点数在内存中使用的大小,而不是在其文本表示中。从文件读取时,您应该分配一个缓冲区以包含整行文本格式。在您的情况下,一个不错的选择可能是:
int bufsize = cols * (3 + DBL_MANT_DIG - DBL_MIN_EXP + 1) + 1;
(请参阅此处了解为什么该值以及 #include
所需的内容:What is the maximum length in chars needed to represent any double value?。尾随 + 1
用于说明换行符,fgets()
确实读取并包含在缓冲区中。)
但这假设输入文件中没有格式错误,因此您可能需要为该值添加一些额外的松弛度。
获得该值后,请在 malloc()
和 fgets()
中使用它:
char *rowbuffer=malloc(bufsize);
i=0;
while(fgets(rowbuffer,bufsize,fp) !=NULL)
...
附带说明一下,您的输入文件看起来使用scanf()
可以更好地阅读。
【讨论】:
感谢您的 cmets。我认为使用bufsize
以这种方式保留的内存将比实际使用的要多得多。关于我的 csv 文件的两件事是,我没有关于它有多少行和列的先前信息 - 也可以是 n 数千或更多。其次,各个值中使用的精度可能会有所不同,例如 0.124 或可以是 0.001204。
@Kaur:嗯,这在很大程度上取决于数据在您要读取的文件中的组织方式。如果行是几十个字符宽(比如说 80-100),这是我在你的情况下假设的,使用行缓冲区通常是可以承受的,并且在阅读完成后你甚至不需要它。如果行可以(多)长,和/或如果您不知道最大长度,您将不得不求助于一种不同的方法,允许您一次读取一个值,例如我的 scanf()
方法建议。你看过了吗?
@Kaur:关于不同的精度,你有没有麻烦看看我给你的链接?
是的,先生,我当天就费心去看了。我已经使用fscanf
实现了它,因为我正在处理大型数据文件并且内存使用是一个限制。您的 cmets 确实有助于清晰。我不确定是否应该将我的解决方案放在这里,或者我应该保持原样。
当然,您可以为自己的问题写一个新答案。请记住接受对你有用的答案。【参考方案2】:
您的编码问题出在:
fgets(rowbuffer,sizeof(rowbuffer),fp)
sizeof(rowbuffer)
只会给你指针的大小,而不是分配给指针的内存大小。
要解决此问题,您需要向fgets()
提供适当大小的已分配内存 [cols * ( sizeof(float)+sizeof(char)
]。
您的逻辑问题出在:
您假设float
值的打印表示将占用与float
变量相同的内存量。不,那不是真的。在打印的表示中,每个数字(包括小数点和小数点后的任何前导或尾随0
)将消耗一个字节的内存。在为目标缓冲区分配内存时应牢记这一点。
【讨论】:
但是,分配的内存大小看起来不适合预期用途。 @SukkoPera 是的。正在更新我的答案。 :-)以上是关于fgets没有读取C中的完整行的主要内容,如果未能解决你的问题,请参考以下文章
C语言fgets()函数(以指定长度读取文件中的字符,并存入字符数组变量中)
C 语言文件操作 ( 按照文本行的方式读写文件 | fgets 函数 | fputs 函数 )