6

是否有getline使用fread(块 I/O)而不是fgetc(字符 I/O)的函数?

通过 .逐字符读取文件会降低性能fgetc。我们认为为了提高性能,我们可以freadgetline. 但是,这会引入超出行尾的潜在不良影响。至少,这将需要实现getline来跟踪文件的“未读”部分,这需要超越 ANSI C FILE 语义的抽象。这不是我们想要自己实现的东西!

我们已经分析了我们的应用程序,性能缓慢与我们通过fgetc. 相比之下,其余的开销实际上是微不足道的。我们总是从头到尾按顺序读取文件的每一行,并且我们可以在读取期间锁定整个文件。这可能使fread-basedgetline更容易实现。

那么,是否存在getline使用fread(块 I/O)而不是fgetc(字符 I/O)的函数?我们很确定它确实如此,但如果不是,我们应该如何实现它?

更新找到了一篇有用的文章,在 C 中处理用户输入,作者是 Paul Hsieh。这是一种fgetc基于 - 的方法,但它对替代方案进行了有趣的讨论(从有多糟糕开始gets,然后讨论fgets):

另一方面,C 程序员(甚至那些被认为有经验的程序员)的共同反驳是,应该使用fgets()作为替代方案。当然,fgets()本身并不能真正处理用户输入。除了有一个奇怪的字符串终止条件(遇到 \n 或 EOF,但不是 \0)之外,当缓冲区达到容量时选择终止的机制是简单地突然停止fgets()操作并 \0 终止它。所以如果用户输入超过了预分配缓冲区的长度,fgets()返回部分结果。处理这个程序员有几个选择;1) 简单地处理被截断的用户输入(当他们提供输入时,无法向用户反馈输入已被截断) 2) 模拟一个可增长的字符数组并通过对fgets()的连续调用来填充它. 第一个解决方案对于可变长度的用户输入几乎总是一个非常糟糕的解决方案,因为缓冲区在大多数情况下不可避免地会太大,因为它试图捕获太多的普通情况,而对于不寻常的情况来说太小了。第二种解决方案很好,只是正确实施可能很复杂。两者都没有处理fgets关于 '\0' 的奇怪行为。

留给读者的练习:为了确定调用fgets()实际读取了多少字节,可以尝试扫描,就像它所做的那样,扫描 '\n' 并跳过任何 '\0' 而不超过传递给fgets()的大小。解释为什么这对于流的最后一行是不够的。ftell()的哪些弱点阻止它完全解决这个问题?

留给读者的练习:通过在每次调用fgets()之间用非零值覆盖整个缓冲区来解决确定fgets()消耗的数据长度的问题。

因此,对于fgets(),我们只能选择编写大量代码并使用与 C 库的其余部分不一致的行终止条件,或者具有任意截止条件。如果这还不够好,那我们还剩下什么?scanf()以一种无法分离的方式将解析与读取混合在一起,并且fread()将读取字符串的末尾。简而言之,C 库让我们一无所有。我们被迫直接基于fgetc()滚动我们自己的。所以让我们试一试。

那么,是否存在getline基于fgets(并且不截断输入)的函数?

4

2 回答 2

5

不要使用fread. 使用fgets. 我认为这是一个家庭作业/课堂项目问题,所以我没有提供完整的答案,但如果你说不是,我会提供更多建议。使用 pure 绝对可以提供 GNU 样式的 100% 的语义getline,包括嵌入的空字节,fgets但这需要一些聪明的思考。

好的,更新,因为这不是家庭作业:

  • memset你的缓冲区到'\n'.
  • 使用fgets.
  • 用于memchr查找第一个'\n'.
  • 如果没有'\n'找到,则该行比您的缓冲区长。扩大缓冲区,用 填充新部分'\n',然后fgets进入新部分,必要时重复。
  • 如果后面的字符'\n''\0',则fgets由于到达行尾而终止。
  • 否则,fgets由于到达 EOF 而终止,'\n'从你的 中剩余memset,前一个字符是fgets写入的终止 null,之前的字符是实际读取数据的最后一个字符。

如果您不关心嵌入空值的支持行(无论哪种方式,空值都不会终止读取;它只是您读入行的一部分),则可以消除memsetand 使用。strlenmemchr

还有一种方法可以使用fscanf和说明"%123[^\n]"符(123缓冲区限制在哪里)做同样的事情,这使您可以灵活地停止在非换行符处(ala GNU getdelim)。但是,除非您的系统具有非常奇特的scanf实现,否则它可能会很慢。

于 2010-12-10T17:04:49.290 回答
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
    ...
} 
于 2010-12-10T22:06:23.050 回答