【问题标题】:Using fgets( ) - the most memory efficient way使用 fgets() - 最节省内存的方法
【发布时间】:2015-09-11 21:25:22
【问题描述】:

环顾网上,我还没有找到与fgets() 字符指针数组(char *restrict s)在有效内存分配方面相关的明确答案。

简介:char *fgets(char *restrict s, int n, FILE *restrict stream);

看着fgets() - specification

根据我对本规范的理解,您应该根据<limits.h> - specification 中定义的LINE_MAX 宏进行分配,因为您不知道每行有多少个字符。

如果我运行 - printf("LINE_MAX BYTES: %d\n", LINE_MAX);,结果是 2048

话虽如此,声明char *line[LINE_MAX] - 或- char *line[2048] 对我来说似乎效率低下,但是,这可能是最好的方法吗?

//程序

#include <stdio.h>                                                                                                  
#include <limits.h>                                                                                                 
int main(void){                                                                                                     
    char line[LINE_MAX];                                                                                   
    FILE *fp = fopen("file.txt", "r");                                                                          
    while(fgets(line, LINE_MAX + 1, fp)){                                                                           
        printf("%s", line);                                                                               
    }                                                                                                    
    fclose(fp);                                                                                                 
    return(0); 
}  

//文件(文件.txt)

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. 
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. 

【问题讨论】:

  • malloc 一些内存,一个字符一个字符地读取,如果没有足够的缓冲区空间,则使用realloc,添加一个百分比或一个固定数量的大小。
  • malloc(),通过fgets() 读取,如果您在到达行尾之前填充缓冲区,则realloc() 并读取更多内容。
  • 实际上,在大多数现代环境中,2048 字节并不是那么多。当然还不够,我会流汗。真正的问题是,我认为依赖不超过 LINE_MAX 个字符的行是不安全的。
  • 另外,如果您使用静态分配,请不要忘记允许LINE_MAX + 1 字符,因为您也需要为字符串终止符留出空间。
  • @JordanDavis,您首先要猜测“合理”行可以有多长,然后分配那么多。调用fgets()后,如果成功,可以通过strlen()判断行长。或者,您可以将缓冲区末尾的字节初始化为\x7f\7f,并在fgets() 之后检查它是否以\0 结尾,前面有\n 以外的其他内容。如果是这样,那么fgets() 耗尽了缓冲区,你需要扩大它。

标签: c linux memory buffer fgets


【解决方案1】:

除非您的文件有一些特殊属性,否则分配LINE_MAX 字节是最好的方法。尽管您可能会在那里浪费一些内存,但自动内存中的分配确实很便宜,因为空间被“保留”多于“分配”,大多数现代架构都有大量硬件支持。

另一方面,如果您知道由于文件格式的原因,行不能超过一定长度MY_LINE,您可以使用MY_LINE+2* 作为您的限制:

char line[MY_LINE+2];

例如,一个程序读取uuendode格式的文件,每行最多需要62个字符,所以它可以定义

#define MY_LINE 62

* 假设您在 UNIX 上,您需要空间用于 '\n''\0',因此需要 +2 部分。如果您使用的是 Windows,请执行 +3 以容纳额外的 '\r' 字符。

【讨论】:

  • 对于我的情况,我不知道行的大​​小,我将从一个在行大小方面具有可变长度的 url 获取文件......所以我希望它是健壮的处理大小方面的变量,但我也在考虑效率。我认为动态分配将是最好的解决方案,基于每行方案进行分配。
  • @JordanDavis 这里的问题是在阅读之前您不知道行的长度。此外,malloc 通常比自动(AKA“堆栈”)分配慢几个数量级。最重要的是,如果中间的代码也进行分配,对每一行使用malloc/free 可能会导致碎片。
  • @JordanDavis 可以逐个字符地阅读,但是练习太慢了。你可以分配一些小的缓冲区——比如说,100 个字符,读入它,直到你得到少于一个充满字符的缓冲区,或者字符串的最后一个字符恰好是'\n'。如果你得到一个完整的缓冲区,最后没有'\n',则将 100 添加到你的运行总数中,然后继续阅读。最终,您要么到达文件末尾,要么到达正文中某处带有'\n' 的字符串。将该位置添加到最终结果的总和中。
  • 在 Windows/DOS 系统上,'\n' 实际上是 2 个字符,所以应该是 +3,而不是 +2
  • @user3629249 已编辑。顺便说一句,如果您想添加类似的小东西,请随时编辑答案。
【解决方案2】:

以下代码:

cleanly compiles
performs error checking

如果一行太长,部分行将被回显,然后下一次调用 fgets() 将获得更多的行,这将被回显,等等。不会丢失任何输出,也不会执行未定义的行为。

您可以使用 'getline()' 代替 fgets(),但何苦呢。使用 getline() 将使代码更复杂,而无需添加任何功能,并且(正如其他人所评论的)2048 字节缓冲区在当今的计算机上是微不足道的。

如果您真的担心缓冲区大小。以下可以为 linux/mac 使用小至 2 个字节的缓冲区(对于 windows/DOS 为 3 个字节)并且仍然可以正常工作

#include <stdio.h>
#include <stdlib.h>

#define LINE_MAX (2048)

int main(void)
{
#include <stdio.h>
#include <stdlib.h>

#define LINE_MAX (2048)

int main(void)
{

    char line[LINE_MAX];

    FILE *fp = NULL;
    if( NULL ==( fp = fopen("file.txt", "r") ) )
    {
        perror( "fopen for file.txt for read failed");
        exit( EXIT_FAILURE );
    }

    while( fgets(line, LINE_MAX, fp) )
    {
        printf("%s\n", line);
    }

    fclose(fp);
    return(0);
}

    char line[LINE_MAX];

    FILE *fp = NULL;
    if( NULL ==( fp = fopen("file.txt", "r") ) )
    {
        perror( "fopen for file.txt for read failed");
        exit( EXIT_FAILURE );
    }

    while( fgets(line, LINE_MAX, fp) )
    {
        printf("%s", line);
    }

    fclose(fp);
    return(0);
}

【讨论】:

  • 看起来不错,不过相信你贴了两次,看看吧。
  • 我也注意到了一些东西,无论是在我的代码中还是在你的代码中...... line[LINE_MAX] 不应该真的是一个指针,因为在概要中它将它列为 char *restrict s 吗?跨度>
  • @JordanDavis,什么简介?对于'fgets()'?第一个参数是指向缓冲区的指针。在 C 中,数组名称降级为缓冲区第一个字节的地址,因此,没有 'line[LINE_MAX]' 不应该是指针。感谢您注意到双重粘贴,只是为了表明,当我累了时,我犯的错误超过(通常数量)
  • 是的fgets(),fgets() 的第一个参数是char *restrict s 我明白什么是char *pointer,但是为什么那里有一个“限制”?并且也没有定义指针数组,而是创建了一个字符数组,所以我们真的应该像我们一样声明字符数组line[LINE_MAX],然后设置一个指向它的指针,称为s(出于示例目的),不是吗?跨度>
猜你喜欢
  • 2010-10-22
  • 2013-10-30
  • 1970-01-01
  • 1970-01-01
  • 2011-10-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多