今天写一个读取文件中字符串的函数,理论上应该是很简单的,但是写的时候发现输出的结果总是比文件中的内容少一个字符,并且通过排查,问题就是出在 fgets 的第二个参数 size 上,而之所以出现这个问题,是因为网上大部分,包括英文的独立博客中的文章都在 fgets 的介绍上,对 size 这个参数的描述不清晰,比较模糊,容易让人理解错误,而他们举的例子往往会掩盖由此导致的问题,下面就来看看。
假设我有一个文档(example.txt),文档中有一个字符串 ABCDEFG,我的目标就是通过 fgets 读取这个字符串,错误的写法如下,我将 fopen,fclose 这些内容也附加到这段代码中,但删减了错误处理的部分。
先展示一下 fgets 的函数原型
char * fgets ( char * str, int num, FILE * stream );
 
错误的代码示例
void LoadFile()
{char *cString  = NULL;int	ulFileSize = 0;FILE       *fp = NULL;// open filefp = fopen("example.txt", "rb");// move file cursor to end of filefseek(fp, 0, SEEK_END);// get file size, ftell return BytesulFileSize = (int)ftell(fp); // 7// malloc for stringcString  = (char*)malloc(ulFileSize);// move file cursor to the beginning of the filefseek(fp, 0, SEEK_SET);// read stringfgets(cString, ulFileSize, fp); // ABCDEF// freefree(cString  ); cString  = NULL;// close filefclose(fp); fp = NULL;
}
 
正如我在上面代码 fgets 那一行的注释,cString 中实际上只拿到了 ABCDEF,而缺失了 G。
原因在于 fgets 的第二个参数 size,虽然表示的是要写入到 str 中的总大小(字符数量),但它包含了终止符。
拿本例来说,虽然 size 为 7, str 指针开辟的动态内存空间也为 7 个字节,但实际上,fgets 往 str 中写的内容是 ABCDEF 6 个字符,外加一个终止符 "\n",这就导致 G 没有被真正写入 str 中。
所以正确的做法是,在开辟内存的时候要预留一个给终止符的位置,以及在 fgets 的 size 部分预留一个给终止符的位置。
正确的代码,这里只展示上面那段代码需要修改的部分
	// malloc for stringcString  = (char*)malloc(ulFileSize + 1);// read stringfgets(cString, ulFileSize + 1, fp); // ABCDEFG
 
这样就可以正确拿到 ABCDEFG 了。