【问题标题】:fgetc(): Reading and storing a string of unknown lengthfgetc():读取并存储未知长度的字符串
【发布时间】:2011-05-14 02:38:03
【问题描述】:

我需要做的是:

  • 打开一个文件(使用 fopen())
  • 读取学生的姓名(使用 fgetc())
  • 将该名称存储在结构的某些部分中

我遇到的问题是我需要将任意长字符串读入名称,我不知道如何存储该字符串而不浪费内存(或写入未分配的内存)。

编辑

我的第一个想法是分配一个 1 字节 (char) 内存块,然后在需要更多字节时调用 realloc() ,但这似乎效率不高。或者,如果数组已满,我可以将其加倍,然后最后将字符复制到确切大小的新内存块中。

【问题讨论】:

  • 您对 realloc 将大小加倍的编辑建议对我来说似乎很好。您可以从一个可能足够大的数组开始(假设您知道它是一个学生姓名)并在大多数情况下避免重新分配。 Supercalifragilisticsuperalidoscious Antidisestablishmentarianism 先生不会去你的学校。
  • @Paul- 你确定他没有?
  • 你不需要将字符复制到一个大小正好合适的新内存中——你可以在最后再次使用realloc() 将分配的块修剪到合适的大小。

标签: c


【解决方案1】:

您可以逐个字符地读取字符串,直到找到结尾,然后倒回到开头,分配一个正确大小的缓冲区,然后将其重新读入其中,但除非您使用的是小型嵌入式系统,否则这是可能很傻。一方面,fgetc、fread 等函数无论如何都会在 O/S 中创建缓冲区。

您可以分配一个足够大的临时缓冲区,使用长度有限的读取(出于安全考虑),然后分配一个精确大小的缓冲区以将其复制到其中。您可能希望在堆栈上而不是通过 malloc 分配临时缓冲区,除非您认为它可能超出您的可用堆栈空间。

如果您正在为小型系统编写单线程代码,您可以在启动时或静态分配一个暂存缓冲区,并将其重新用于多种用途 - 但要非常小心,您的使用不能重叠!

鉴于大多数系统的实现复杂性,除非您真正研究事物的工作原理,否则完全有可能编写内存优化代码,实际上比简单的方法占用更多的内存。变量初始化可能是另一个令人惊讶的浪费。

【讨论】:

    【解决方案2】:

    不要担心浪费 100 或 1000 个字节,这对于所有名称来说可能已经足够长了。 我可能只是将您正在读取的缓冲区放入堆栈。

    不要担心会写到缓冲区的末尾。即缓冲区溢出。防止这种情况发生的程序!

    当您将名称存储到您的结构中时,您可以分配一个缓冲区来存储您需要的确切长度的名称(不要忘记为空终止符添加一个额外的字节)。

    但是,如果您真的必须存储任何长度的名称,那么您可以使用 realloc 来完成。 即用一些大小的 malloc 分配一个缓冲区,比如 50 字节。

    然后当你需要更多空间时,使用 realloc 来增加它的长度。以 50 字节为单位增加块的长度,并使用 int 跟踪它的大小,以便您知道何时需要再次增长它。在某些时候,您将不得不决定该缓冲区将持续多长时间,因为它不能无限增长。

    【讨论】:

    • 你真的应该担心缓冲区溢出,除非你正在处理最快和肮脏的测试应用程序,或者可以绝对确定字符串不能大于你的缓冲区
    【解决方案3】:

    我的建议是分配一个足够大的缓冲区:

    char name_buffer [ 80 ];
    

    通常,大多数名称(至少是常见的英文名称)的大小都小于 80 个字符。如果您觉得您可能需要更多空间,请务必分配更多空间。

    保留一个计数器变量以了解您已经读入缓冲区的字符数:

    int chars_read = 0; /* most compilers will init to 0 for you, but always good to be explicit */
    

    此时,使用 fgetc() 逐个字符地读取,直到您到达文件末尾标记或读取 80 个字符(实际上是 79 个字符,因为您需要为空终止符留出空间)。将您读取的每个字符存储到缓冲区中,并增加您的计数器变量。

    while ( ( chars_read < 80 ) && ( !feof( stdin ) ) ) {
      name_buffer [ chars_read ] = fgetc ( stdin );
      chars_read++;
    }
    if ( chars_read < 80 )
      name_buffer [ chars_read ] = '\0'; /* terminating null character */
    

    我在这里假设您正在阅读stdin。更完整的示例还将检查错误,验证您从流中读取的字符是否对人名有效(例如,没有数字)等。如果您尝试读取的数据多于分配空间的数据,请打印向控制台发送错误消息。

    我理解希望保持尽可能小的缓冲区并且只分配您需要的内容,但学习如何编程的一部分是了解代码/数据大小、效率和代码可读性方面的权衡。您可以mallocrealloc,但它使代码比必要的复杂得多,并且它引入了可能出现错误的地方 - NULL 指针、数组索引越界错误等。对于大多数实际情况,分配足以满足您的数据需求的内容以及少量的喘息空间。如果您发现遇到很多数据超出缓冲区大小的情况,请调整缓冲区以适应它 - 这就是调试和测试用例的用途。

    【讨论】:

      猜你喜欢
      • 2020-04-10
      • 2013-01-23
      • 1970-01-01
      • 2021-12-04
      • 1970-01-01
      • 2014-01-10
      相关资源
      最近更新 更多