用std::ifstream读二进制文件最高效方式:先seekg+tellg获取字节数,除以sizeof(T)得元素数n,resize vector,再read()一次性读入并检查gcount();T须为trivially copyable。

用 std::ifstream 读二进制文件到 std::vector
如果文件是用 std::ofstream 以二进制方式写入的(比如直接 write() 整个 vector),那反向读取最高效也最安全。
- 先读文件大小,用
seekg(0, std::ios::end)+tellg()获取字节数,再除以单个元素大小,得到元素个数 - 提前分配
vector容量:vec.resize(n),避免多次内存重分配 - 用
read()一次性读入:is.read(reinterpret_cast<char>(vec.data()), n * sizeof(T))</char> - 务必检查
is.gcount() == n * sizeof(T),否则可能读半截或失败(比如文件被截断、权限不足) - 注意:
T必须是 trivially copyable 类型(如int、double、struct不含指针/虚函数/非平凡构造函数)
从文本文件按行解析数字进 std::vector<int></int>
常见于配置、测试数据或简单日志——每行一个整数,或空格/制表符分隔。
- 别用
operator>>直接读到int变量再push_back,容易因格式错位(比如某行多出字母)导致流进入failbit,后续所有读取静默失败 - 更稳的做法:用
std::string先读整行,再用std::stringstream或std::from_chars(C++17)逐个解析 -
std::from_chars更快且不依赖 locale,但不跳过空白,需手动find_first_not_of(" \t\n") - 示例片段:
std::string line; while (std::getline(in, line)) { std::string_view sv = line; size_t pos = 0; int x; while (pos < sv.size()) { auto [ptr, ec] = std::from_chars(sv.data() + pos, sv.data() + sv.size(), x); if (ec == std::errc{}) { vec.push_back(x); pos = ptr - sv.data(); } else { break; // 跳过非法部分 } } }
std::vector 内存布局和文件对齐问题
二进制读写时,结构体成员顺序、填充字节、编译器对齐策略都会让文件不可跨平台、甚至跨编译器复用。
- 用
#pragma pack(1)或[[gnu::packed]]强制紧凑布局,但仅限同一编译环境内使用 - 不要假设
sizeof(std::vector<t>)</t>是可序列化的——它只存指针、size、capacity,真正数据在堆上 - 想持久化结构体数组?必须手写字段级读写,或用
memcpy拷贝每个对象(前提是类型满足 trivially copyable) - Windows 和 Linux 下
off_t大小不同,tellg()返回值转size_t前要确认是否溢出(尤其大文件 >2GB)
错误信息里常见的 ios_base::failure 和 bad_alloc
不是所有读取失败都报错明显;有些只是 vector 空着返回,或者程序崩溃在 data() 解引用。
立即学习“C++免费学习笔记(深入)”;
-
ios_base::failure通常来自exceptions()开启后流操作失败(比如磁盘满、路径不存在),但默认是关闭的——记得调is.exceptions(std::ios::failbit | std::ios::badbit) -
bad_alloc往往发生在resize()时请求超大内存(比如误把文件大小当元素个数,而没除以sizeof(T)) - 用
in.peek() == EOF判断空文件比in.eof()更可靠;后者只在尝试读取失败后才置位 - 打开失败不等于
!in立即为真——可能延迟到第一次读才触发,所以建议显式if (!in.is_open())或if (!in)在open()后立刻检查
二进制序列化看似简单,但类型边界、对齐、端序、异常路径覆盖,哪一环松动都容易在某个客户机器上突然出问题。

















