fgetc():读取和存储未知长度的字符串

时间:2010-11-22 21:08:55

标签: c

我需要为作业做些什么:

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

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

修改

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

3 个答案:

答案 0 :(得分:5)

不要担心浪费100或1000字节,这可能对所有名称都足够长。 我可能只是把你正在阅读的缓冲区放在堆栈中。

执行担心写入缓冲区的末尾。即缓冲区溢出。计划,以防止这种情况!

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

但如果你真的必须存储任何长度的名字,那么你可以用realloc来做。 即分配一个大小为50字节的malloc的缓冲区。

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

答案 1 :(得分:2)

你可以逐个字符地读取字符串,直到你找到结束,然后回到开头,分配一个合适大小的缓冲区,并重新读入它,但除非你在一个很小的嵌入式系统上这是可能很傻。首先,fgetc,fread等函数无论如何都会在O / S中创建缓冲区。

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

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

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

答案 2 :(得分:2)

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

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指针,数组索引越界错误等。在大多数实际案例中,分配足以满足您的数据需求以及少量呼吸空间的情况。如果您发现很多情况下数据超出了缓冲区的大小,请调整缓冲区以适应它 - 这就是调试和测试用例的用途。

相关问题