用 std::ifstream 读二进制文件最稳妥方式是 read() 配合 binary 模式:先 seekg/tellg 获取大小,分配 size+1 字节数组(末置 '\0'),再 read() 并用 gcount() 校验实际读取量。

用 std::ifstream 读到 char 数组最稳妥的方式
直接用 read() 是唯一能保证字节级控制、不丢数据、不额外解析的方法。别信 getline() 或 operator>>,它们会跳过空白、截断换行、甚至把 \0 当结束——而你只是想原样搬文件内容。
实操要点:
- 先用
seekg(0, std::ios::end)和tellg()获取文件大小,避免动态扩容或预估错误 - 分配足够空间:大小 + 1(留位置放末尾
\0,方便后续当 C 字符串用) - 回到开头:
seekg(0, std::ios::beg),再调read(buf, size) - 务必检查
gcount()—— 它返回实际读取字节数,可能小于预期(比如磁盘错误、权限不足)
<pre class="brush:php;toolbar:false;">std::ifstream file("data.bin", std::ios::binary);
file.seekg(0, std::ios::end);
size_t size = file.tellg();
file.seekg(0, std::ios::beg);
char* buf = new char[size + 1];
buf[size] = '\0'; // 确保可安全当字符串用
file.read(buf, size);
if (file.gcount() != static_cast<:streamsize>(size)) {
// 读取不完整,处理错误
}
</:streamsize>为什么不能用 fopen
+ fread?能用,但 C 风格接口在 C++ 里容易漏掉关键收尾动作,尤其跨平台时。
常见翻车点:
-
fopen后没检查返回值是否为nullptr,Windows 上路径含中文、Linux 上权限不足都会静默失败 -
fread返回值是成功读取的“项数”,不是字节数 —— 若你写fread(buf, 1, size, fp),它返回size才对;但若误写成fread(buf, size, 1, fp),就只读一次且极易返回 0 - 忘记
fclose(fp),小文件不明显,大循环里会快速耗尽文件描述符(Linux 报Too many open files)
std::vector<char></char> 比裸 char* 更省心
除非你在写嵌入式或性能极端敏感的模块,否则别手动 new/delete char 数组。内存泄漏、越界、忘记初始化 <p>除非你在写嵌入式或性能极端敏感的模块,否则别手动 <code>new/delete char 数组。内存泄漏、越界、忘记初始化 \0 全是高频问题。
换成 std::vector 后:
- 大小自动管理:
vec.resize(size)后直接传&vec[0]给read() - 作用域结束自动释放,不用操心
delete[] - 可直接用
vec.data()(C++11 起)获取原始指针,兼容所有 C 接口 - 如果后续要转成
std::string,std::string(vec.begin(), vec.end())能正确保留中间的\0
二进制文件 vs 文本文件:开关没设对就读废了
Windows 下换行符是 \r\n,Linux 是 \n。如果你读的是图片、音频、序列化数据,或者自己写的二进制格式,必须加 std::ios::binary 标志。
不加的后果:
- Windows 上
read()可能提前终止在\r处(被当成文本行尾) - 某些编译器会自动把
\r\n转成单个\n,导致缓冲区长度和实际文件字节数对不上 - 哪怕你只读 ASCII 文本,只要文件里可能有
\0(比如协议头),也得开 binary 模式
一句话:只要不确定内容全是可打印字符+标准换行,一律加 std::ios::binary。
真正容易被忽略的是:std::ifstream 默认是文本模式,这个默认值不会因为你在 Linux 上运行就变,也不会因为你读的是 .bin 后缀就自动识别 —— 它只认 flag,不猜意图。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











