今天写一个读取文件中字符串的函数,理论上应该是很简单的,但是写的时候发现输出的结果总是比文件中的内容少一个字符,并且通过排查,问题就是出在 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 了。