You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 07:25:25