fread是读二进制文件到字符数组最直接的方式,需以"rb"模式打开、检查返回值、预留'\0'空间;推荐用std::vector<char>或std::string配合binary标志读取,避免fgets等文本函数。

用 fread 读二进制文件到字符数组最直接
如果文件内容不含字符串终止符 '\0',且你明确知道大小(比如图片、音频、配置块),fread 是最轻量、最可控的方式。它不解析换行、不跳过空白、不尝试转码,就是原样拷贝字节。
常见错误是忽略返回值或误用缓冲区大小:
-
fread返回实际读取的**元素个数**,不是字节数(除非你传sizeof(char)作为元素大小) - 字符数组必须预留
\0位置,否则后续当 C 字符串用会越界(除非你本就不打算当字符串用) - 文件需以
"rb"模式打开,避免 Windows 下\r\n被自动转成\n
FILE* fp = fopen("data.bin", "rb");
if (!fp) return;
fseek(fp, 0, SEEK_END);
long size = ftell(fp);
rewind(fp);
<p>char* buf = new char[size + 1]; // +1 for '\0'
size_t n = fread(buf, 1, size, fp); // 读 n 字节
buf[n] = '\0'; // 安全补零
fclose(fp);
// …使用 buf…
delete[] buf;读文本文件建议用 std::vector<char> 配合 fread 或 std::ifstream::read
字符数组(char[])本身不带长度信息,手动管理 new[]/delete[] 容易出错;而 std::vector<char> 自动管理内存、自带 .data() 和 .size(),更适合现代 C++ 场景。
用 std::ifstream 时注意:默认按文本模式打开,Windows 下可能破坏二进制内容;必须显式用 std::ios::binary 标志:
立即学习“C++免费学习笔记(深入)”;
-
is.read(buf.data(), buf.size())不会自动加\0,也不检查是否读满 —— 你要自己用is.gcount()拿实际字节数 - 提前
reserve()不能代替resize();read()只往已有元素里写,不会扩容
std::ifstream is("log.txt", std::ios::binary);
is.seekg(0, std::ios::end);
size_t size = is.tellg();
is.seekg(0);
<p>std::vector<char> buf(size);
is.read(buf.data(), size);
size_t n = is.gcount(); // 实际读取字节数,可能 < size(如文件被截断)
std::string 也能存二进制数据,但要注意构造方式
std::string 不是只能存文本 —— 它内部就是 char 序列,支持嵌入 \0,只要不用 c_str() 当 C 字符串传给 C 接口就行。
错误写法:std::string s = std::string(buf, len) 是安全的;但 std::string s(buf)(只传指针)会停在第一个 \0,丢数据。
- 从文件读取后,用
s.assign(data, size)或构造函数std::string(data, size)显式指定长度 -
s.data()和s.c_str()在 C++11 后都指向同一块内存,但c_str()保证末尾有\0(可能触发复制) - 若后续要传给 C 函数(如
write()、send()),直接用s.data()+s.size()最高效
别用 fgets 或 operator>> 读整个文件
它们是为逐行或逐词设计的,不是为“整块加载”准备的:
-
fgets遇到\0、换行、EOF 都会停,且会把换行符塞进缓冲区,长度难控 -
operator>>跳过所有空白(包括\0、\r、\t),根本没法保真读二进制 - 想用
std::getline拼接?性能差、内存碎片多、换行符处理逻辑复杂,纯属绕路
真正需要按行处理时再用它们;一次性载入就老实用 fread 或 istream::read。
最容易被忽略的是:文件大小可能动态变化(比如日志文件正在被写入),ftell/tellg 的结果未必等于最终可读字节数 —— 所以实际读取后一定要检查返回值,不能无条件相信预估大小。


















