C语言中ftell()返回文件长度与实际读取长度不一致问题求助
文本模式下ftell()返回值与实际读取长度不符的原因及解决方法
问题根源
你遇到的问题是Windows平台C标准库的文本模式换行符转换导致的:
- 当用文本模式(
"r")打开文件时,库会自动将磁盘文件中的Windows换行符\r\n转换为内存中的\n,每一对\r\n会被替换成单个\n,相当于每个换行少了1个字节。 ftell(fp)在文本模式下返回的是文件在磁盘上的原始字节数(包含所有\r\n),但fread读取到内存的是转换后的内容,实际字节数会比ftell的结果少(少的数量等于文件中\r\n的个数)。- 这就导致你用
ftell得到的fileLen去分配缓冲区,而fread只填满了其中sizetLen字节,剩下的缓冲区空间如果未被正确处理,就会出现冗余内容(你的代码用了calloc会初始化为0,但如果是malloc就会是垃圾数据,表现为前面内容的重复或乱码)。
解决方法
方案1:用二进制模式打开文件(推荐)
将fopen_s的打开模式改为"rb",这样库不会做任何换行符转换,ftell返回的长度和fread实际读取的长度完全一致:
int datReadFileToBuf(char* fn, BYTE** buf) { int fileLen = 0; if (*buf != NULL) return -1; // ERROR: The buffer should be NULL. FILE* fp = NULL; if (fopen_s(&fp, fn, "rb") != 0) return -2; // 改为二进制模式 fseek(fp, 0, SEEK_END); fileLen = ftell(fp); rewind(fp); if ((*buf = (BYTE*)calloc(fileLen, 1)) == NULL) return -3; size_t sizetLen = fread(*buf, 1, fileLen, fp); // 此时fileLen和sizetLen应该相等(除非读取出错) fclose(fp); return fileLen; // 现在可以安全返回fileLen }
SVG本身是文本格式,但二进制模式打开不会破坏内容,只是保留原始的\r\n换行符,而SVG解析器对换行符的格式不敏感,完全可以正常处理。
方案2:保留文本模式,以fread返回值为准
如果必须用文本模式打开(比如需要处理其他文本转换逻辑),就继续使用你当前的临时方案:返回fread的返回值sizetLen,后续处理只使用缓冲区的前sizetLen字节,忽略剩余部分。这种方式虽然可行,但每次都要额外处理长度差异,不如二进制模式直接。
补充说明
- 这个问题只在Windows平台存在,Unix/Linux/macOS等系统的文本模式和二进制模式没有区别,因为它们的换行符本身就是
\n,不需要转换。 - 文本模式下,
ftell的返回值不能直接等同于文件的字符数或内存中存储的字节数,因为存在字符转换的情况,这是C标准库的规定。
内容的提问来源于stack exchange,提问作者csharp
相关产品推荐
相关产品推荐

