Jul*_*erg 6 c file-io fgets fread
是否有getline使用fread(块I/O)而不是fgetc(字符I/O)的函数?
通过字符读取文件字符会有性能损失fgetc.我们认为为了提高性能,我们可以fread在内循环中使用块读取getline.然而,这引入了读取超过行尾的潜在不期望的效果.至少,这需要实现getline跟踪文件的"未读"部分,这需要超出ANSI C FILE语义的抽象.这不是我们想要自己实现的东西!
我们已经分析了我们的应用程序,并且由于我们逐个字符地消耗大型文件,因此性能缓慢fgetc.通过比较,其余的开销实际上具有微不足道的成本.我们总是按顺序读取文件的每一行,从头到尾,我们可以在读取期间锁定整个文件.这可能使fread基于getline更容易实现.
那么,是否存在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(并且不截断输入)的函数?
不要用fread.使用fgets.我认为这是一个家庭作业/课堂项目问题所以我没有提供完整的答案,但如果你说不是,我会给出更多的建议.绝对有可能提供100%的GNU风格的语义getline,包括纯粹使用的嵌入式空字节fgets,但它需要一些聪明的思考.
好的,更新,因为这不是作业:
memset你的缓冲区'\n'.fgets.memchr查找第一个'\n'.'\n'找到,则该行比您的缓冲区长.加入缓冲区,填充新部分'\n',然后fgets填入新部分,根据需要重复.'\n'是'\0',则fgets由于到达行的末尾而终止.fgets由于到达EOF而终止,'\n'从你的遗留下来memset,前一个字符是fgets写入的终止空值,之前的字符是实际数据读取的最后一个字符.您可以消除memset和使用strlen到位的memchr,如果你不关心支持线条与嵌入的空值(无论哪种方式,空将不会终止阅读;它只会成为你的读入线的一部分).
还有一种方法可以使用fscanf和说明"%123[^\n]"符(在哪里123是你的缓冲区限制)做同样的事情,这使你可以灵活地停止非换行符(ala GNU getdelim).但是,除非您的系统具有非常精细的scanf实现,否则它可能会很慢.