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中的完整行的主要内容,如果未能解决你的问题,请参考以下文章

fgets()函数详解

试图理解 fgets()

C语言fgets()函数(以指定长度读取文件中的字符,并存入字符数组变量中)

C 语言文件操作 ( 按照文本行的方式读写文件 | fgets 函数 | fputs 函数 )

read.csv 警告“引用字符串中的 EOF”阻止完整读取文件

BLE特性没有一次读取完整的字符串