【问题标题】:C fgets versus fgetc for reading lineC fgets 与 fgetc 用于读取行
【发布时间】:2011-07-08 08:55:39
【问题描述】:

我需要阅读一行文本(以换行符结尾)而不对长度做出假设。所以我现在面临着各种可能性:

  • 使用fgets,每次检查最后一个字符是否为换行符并不断追加到缓冲区
  • 使用fgetc 读取每个字符,偶尔使用realloc 缓冲区

直觉告诉我fgetc 变体可能会更慢,但我还是不明白fgets 在不检查每个字符的情况下如何做到这一点(而且我的直觉并不总是那么好)。线条很大,所以性能很重要。

我想知道每种方法的优缺点。提前谢谢你。

【问题讨论】:

    标签: c io stdio fgets fgetc


    【解决方案1】:

    我建议将 fgets() 与动态内存分配结合使用 - 或者您可以研究 POSIX 2008 标准中的 getline() 接口,并且在更新的 Linux 机器上可用。这为您完成了内存分配工作。您需要密切关注缓冲区长度及其地址 - 因此您甚至可以自己创建一个结构来处理信息。

    虽然fgetc() 也可以工作,但它稍微有点复杂 - 但只是稍微有点。在封面之下,它使用与fgets() 相同的机制。内部可能能够利用更快的操作 - 类似于 strchr() - 当您直接调用 fgetc() 时不可用。

    【讨论】:

    • 使用fgets 实现getline 函数的一个限制是无法同时处理空字节 不以换行符结尾的文件.如果fgets 遇到 EOF 条件并且返回时没有换行符,则只能假设字符串在第一个空字节处结束。 (在其他情况下,您可以通过strchr(buf, '\n') 查找读取停止的位置,或者如果没有'\n',则需要realloc。)
    • 如果文件包含空字节,则它不是文本文件。 (它可能是一个宽字符文件,但是您需要使用宽字符 I/O 函数来读取它。)而fgets() 并不是为处理包含空字节的文件而设计的——正是因为它没有给出可靠的指示它读取了多少字节。如果您的数据文件包含空字节,您应该(可能)不使用fgets() 来读取它。
    • linux.die.net/man/3/getline(返回值部分)似乎表明它可能是有用的东西。这就是我的想法,虽然我想我同意你的看法。现在我想起来了,也许这只是在那里提到的,因为它在使用'\n' 以外的分隔符时可能很有用。
    【解决方案2】:

    您的环境是否提供getline(3) 功能?如果是这样,我会说去吧。

    我看到的最大优势是它自己分配缓冲区(如果你愿意的话),如果缓冲区太小,它会realloc() 你传入的缓冲区。 (所以这意味着你需要传入从malloc() 获得的东西)。

    这摆脱了 fgets/fgetc 的一些痛苦,您可以希望编写实现它的 C 库的人会注意提高它的效率。

    奖励:Linux 上的手册页有一个很好的示例,说明如何以有效的方式使用它。

    【讨论】:

    • 不幸的是(我很抱歉我没有在问题中提到这一点)我需要使用标准的东西:-( getline 函数听起来很有吸引力。
    • 嗯,它是标准的(对于标准的一些定义)。参见The Open Group Base Specifications Issue 7,又名“IEEE Std 1003.1™-2008”,又名“POSIX C 2008”。但不幸的是,标准!=普遍。我感觉到你的痛苦。 getline 很性感 :-)
    • getline() 功能不错; getline() 这个名字是对用户命名空间的严重入侵,它抢占了一个更广泛使用的函数名称(例如,参见 K&R 1 和 2),并具有广泛多样的接口。使用这个名字是一个骇人听闻的决定;提供该功能是一个极好的决定。唯一令人惊讶的是忽略了处理 CRLF 行尾的能力;相关的getdelim() 函数可以处理 CR 或 LF 或 NUL 行结尾,但不能处理 CRLF 行结尾。
    【解决方案3】:

    如果性能对您很重要,您通常希望调用getc 而不是fgetc。该标准试图让getc 更容易实现为宏以避免函数调用开销。

    除此之外,要处理的主要问题可能是您分配缓冲区的策略。大多数人使用固定增量(例如,当/如果我们用完空间时,再分配 128 个字节)。我建议改为使用常量因子,因此如果空间不足,请分配一个缓冲区,例如,之前大小的 1 1/2 倍。

    特别是当getc 被实现为宏时,getcfgets 之间的差异通常非常小,因此您最好专注于其他问题。

    【讨论】:

      【解决方案4】:

      如果您可以设置最大行长度,即使是大的,那么fgets 就可以了。如果不是这样,多个fgets 调用仍然会比多个fgetc 调用更快,因为后者的开销会更大。

      不过,一个更好的答案是,除非您必须这样做,否则不必担心性能差异。如果fgetc足够快,那有什么关系?

      【讨论】:

      • 另请注意,getc 通常是作为宏实现的,因此比fgetc 快,只要小心使用就应该使用(参数不能是表达式)。
      【解决方案5】:

      如果您还没有读到行尾,我会分配一个大缓冲区,然后使用 fgets、检查、重新分配和重复。

      每次读取(通过 fgetc 或 fgets)时,您都在进行需要时间的系统调用,您希望尽量减少发生的次数,因此调用 fgets 的次数更少并且在内存中的迭代速度更快。

      如果您正在从文件中读取,则文件中的mmap()ing 是另一种选择。

      【讨论】:

      • 我必须在系统调用部分与您相矛盾:stdio 库会进行缓冲,因此我认为不会将每个函数调用都转换为系统调用。我可能错了
      • 这是真的,但使用 fgets 他将拥有更细粒度的控制。如果他知道行的平均长度,他可以优化缓冲区长度,而不是 fgetc 会缓冲但完全不知道理想的缓冲区长度。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多