是否有getline
函数使用fread
(块I / O)而不是fgetc
(字符I / O)?
通过fgetc
逐个字符地读取文件会导致性能下降。我们认为,为了提高性能,我们可以在fread
的内部循环中通过getline
使用块读取。然而,这引入了读取超过行尾的潜在不期望的效果。至少,这需要实现getline
来跟踪文件的“未读”部分,这需要超出ANSI C FILE语义的抽象。这不是我们想要自己实现的东西!
我们已经分析了我们的应用程序,并且由于我们通过fgetc
逐个字符地消耗大型文件,因此性能缓慢。通过比较,其余的开销实际上具有微不足道的成本。我们总是按顺序读取文件的每一行,从头到尾,我们可以在读取期间锁定整个文件。这可能使基于fread
的{{1}}更容易实现。
那么,是否存在使用getline
(块I / O)而不是getline
(字符I / O)的fread
函数?我们非常肯定它确实如此,但如果没有,我们应该如何实施呢?
更新 Paul Hsieh发现了一篇有用的文章Handling User Input in C。这是一种基于fgetc
的方法,但它对备选方案进行了有趣的讨论(从fgetc
有多糟糕开始,然后讨论gets
):
另一方面,来自C程序员(甚至是那些经验丰富的人)的常见反驳意味着应该使用 fgets()作为替代。当然, fgets()本身并不能真正处理用户输入。除了有一个奇怪的字符串终止条件(在遇到\ n或EOF,但不是\ 0时),当缓冲区达到容量时选择终止的机制是简单地突然停止 fgets()操作和\ 0终止它。因此,如果用户输入超过预分配缓冲区的长度, fgets()将返回部分结果。处理这个程序员有几个选择; 1)简单地处理截断的用户输入(无法向用户反馈输入已被截断,同时提供输入)2)模拟可增长的字符数组并通过连续调用填充它与fgets()即可。对于可变长度的用户输入,第一种解决方案几乎总是一个非常糟糕的解决方案,因为缓冲区在大多数情况下不可避免地会过大,因为它试图捕获太多普通情况,而对于异常情况则太小。第二种解决方案很好,只是正确实施可能很复杂。对于'\ 0'而言,都不会处理 fgets'奇怪的行为。
练习留给读者:为了确定通过调用 fgets()确实读取了多少字节,可以尝试通过扫描,就像它一样, '\ n'并跳过任何'\ 0',同时不超过传递给 fgets()的大小。解释为什么这对于流的最后一行是不够的。 ftell()的弱点阻止它完全解决这个问题?
练习留给读者:通过在每次调用 fgets()所消耗数据长度的问题>与fgets() 的
因此,对于 fgets(),我们可以选择编写大量代码并使用与C库其余部分不一致的行终止条件,或者进行任意切割-off。如果这还不够好,那么我们还剩下什么? scanf()以无法分离的方式将解析与阅读混合, fread()将读取超出字符串结尾的内容。简而言之,C库没有任何东西。我们被迫直接在 fgetc()的基础上推出自己的。所以让我们试一试。
那么,是否存在基于fgets
(并且不截断输入)的getline
函数?
答案 0 :(得分:5)
请勿使用fread
。使用fgets
。我认为这是一个家庭作业/课堂项目问题所以我没有提供完整的答案,但如果你说不是,我会给出更多的建议。绝对有可能使用纯getline
提供GNU样式fgets
的100%语义,包括嵌入的空字节,但它需要一些聪明的思考。
好的,更新,因为这不是作业:
memset
您的缓冲区为'\n'
。fgets
。memchr
查找第一个'\n'
。'\n'
,则该行长于您的缓冲区。加入缓冲区,将'\n'
和fgets
的新部分填入新部分,并根据需要重复。'\n'
后面的字符为'\0'
,则fgets
因终点而终止。fgets
由于达到EOF而终止,'\n'
从memset
遗留下来,前一个字符是fgets
写的终止空值,以及之前的字符是实际数据读取的最后一个字符。如果您不关心支持嵌入空值的行,则可以取消memset
并使用strlen
代替memchr
(无论哪种方式,null都不会终止读取;它只是你读入行的一部分。)
还有一种方法可以使用fscanf
和"%123[^\n]"
说明符(其中123
是您的缓冲区限制)执行相同操作,这使您可以灵活地停止非换行符字符(ala GNU getdelim
)。但是,除非你的系统有一个非常花哨的scanf
实现,否则它可能很慢。
答案 1 :(得分:1)
fgets与fgetc / setvbuf之间没有太大的性能差异。 尝试:
int c;
FILE *f = fopen("blah.txt","r");
setvbuf(f,NULL,_IOLBF,4096); /* !!! check other values for last parameter in your OS */
while( (c=fgetc(f))!=EOF )
{
if( c=='\n' )
...
else
...
}