使用fgets()

这两天想用一下gprof。拿出几天前写的插入排序代码,排序所有的英文单词。这所有的单词加起来有216,555个之多,作为测试数据足以。

将单词拷贝下来,用Vim切割一番,基本也就单行一个单词了,这样便于直接调用I/O接口读取,比如fgets。很久没有写文件I/O方面的代码,这个函数的细节居然给忘记了,翻开APUE,将 Standard I/O Library 一章扫了一遍,算是复习了一遍。其实man一下fgets也可以找到相关的信息。

然后下面是读文件的代码:

#include <stdio.h>

#define MAX_WORD_LEN 16

int read_string_data(char *file, char dest[][MAX_WORD_LEN], int count) {
        FILE *fp = fopen(file, "rb");
int idx = 1; /* start from 1, to conform to the Vim's line number while output to file. */ char *ret = NULL; for (;;) { ret = fgets(dest[idx], MAX_WORD_LEN, fp); dest[idx][strlen(dest[idx]) - 1] = 0; /* delete '\n' */ if (ret == NULL) { printf("reading line %d error.", idx); break; } idx++; if (idx > count) break; } fclose(fp); }

但是我发现,读出来的单次个数没有216,555,但idx已经满足了退出条件。问题在哪?

一查发现有些单次的字节数会大于16个字节,而且在fgets的时候,会连同行尾的'\n'也读入目标字符串中。这样如果单个单词的字节数超过14个,再加上'\n'以及一个'\0'字节,就已经超过了预设的缓冲区。但是fgets()会保证只读出16个字节,这样,如果'\n'的索引为16,那么fgets()就不会将其读入,在下一次while循环的时候,再将这个'\n'读入下一个缓冲区---然后本次读行操作结束,本次只将'\n'写入了缓冲区,但是idx自加,到最后,由于过长单词对idx的干扰,导致idx提前达到count极值,while()退出。其实将单词长度加长一点就行了。

这就是为何有些单次没有读出来但while()却已经运行了count次数的原因。当fgets()发现缓冲区不够用时,为何不报错呢?对于文本文件而言,其单行长度是没有限制的,所以如果缓冲区不够,直接返回NULL不就行了么?

fgets()究竟为何这样实现,暂且不论,可能是有历史原因的。其实还有一个gets(),这哥们就极端了,只接受一个char*参数,也即不管你给的这个缓冲区是多少(前提是不是NULL,不然segment error),就往里面丢数据,当遇到巨无霸的行数据,当缓冲区不够时,可能就会溢出,然后,然后,然后你就等着程序在莫名其妙的地方crash吧。相比而言,还是fgets()厚道。APUE明言道:

...The gets() function should never be used...it doesn't allow the caller to specify the buffer size. This allow the buffer to overflow...

但是gets()函数却不会将'\n'读进来,这大概是其唯一值得存在的理由了。Unix API以其严谨简明著称,但也可以满足不同的口味,对于不想处理'\n',只想写测试用例的coder而言,用gets()来读一些简单文件,还是蛮惬意的。

posted on 2012-09-25 02:39  coanor 阅读( ...) 评论( ...) 编辑 收藏

转载于:https://www.cnblogs.com/coanor/archive/2012/09/25/2700948.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值