不能直接用jsoncpp或nlohmann/json读大json,因其全量加载导致内存暴涨;应选用simdjson(推荐)或rapidjson sax模式进行流式解析,注意文件编码、bom、打开模式等细节。

为什么不能用 jsoncpp 或 nlohmann/json 直接读大 JSON
因为它们默认是「全量加载」:把整个文件读进内存,再解析成树状对象。一个 2GB 的 JSON 文件,解析后可能占 4–6GB 内存,std::bad_alloc 很快就来了。
真正可行的路只有一条:流式(streaming)解析 —— 边读边处理,不保留完整结构。C++ 没有标准 JSON 流式库,得靠第三方,目前最稳的是 simdjson 和 rapidjson 的 SAX 模式。
simdjson 流式遍历大型 JSON 数组(推荐首选)
simdjson 的 ondemand::parser 是真正的零拷贝流式接口,支持从 std::ifstream 直接迭代,内存占用恒定在 ~16KB 左右(仅缓冲区),比 rapidjson SAX 更易用。
常见场景:日志文件、导出数据、ETL 流水线中按行或按数组元素处理 JSON。
- 必须用二进制模式打开文件:
std::ifstream file("data.json", std::ios::binary) - 调用
parser.iterate(file)返回ondemand::document,它不是数据副本,而是“游标” - 对顶层为数组的 JSON,用
for (auto value : doc.get_array())迭代每个元素 —— 每次只加载当前元素,前一个自动释放 - 如果某字段缺失,
value["field"].get_string()会返回simdjson::NO_SUCH_FIELD错误,别直接解引用
示例片段:
simdjson::ondemand::parser parser;
std::ifstream file("big.json", std::ios::binary);
auto doc = parser.iterate(file);
for (auto tweet : doc.get_array()) {
std::string_view user = tweet["user"]["name"].get_string();
int64_t ts = tweet["created_at"].get_int64(); // 自动类型转换,失败则抛异常
process_tweet(user, ts);
}
rapidjson SAX 模式手动控制解析流程(适合精细状态管理)
当你需要跳过某些嵌套层级、或根据键名动态决定是否深入解析时,SAX(事件驱动)比 simdjson 的 ondemand 更灵活,但代码量多、易出错。
关键点:
- 继承
rapidjson::BaseReaderHandler<:utf8>, YourHandler></:utf8>,重写Key()、String()、StartObject()等回调 - 用
rapidjson::Reader+FileReadStream组合,避免一次性读整个文件到内存 - 注意:
String()回调里的str是临时指针,内容仅在本次回调有效;需深拷贝才能保存 - 遇到超长字符串或深度嵌套时,
SetMaxDepth()和SetMaxStringLength()必须提前设好,否则可能栈溢出或拒绝服务
错误典型现象:ParseError: Invalid value. 常因 BOM 头未跳过,或文件含 UTF-16 编码 —— 必须用 FileReadStream 并指定 sizeof(char),别用 std::string 中转。
文件编码、BOM 和 mmap 的实际取舍
90% 的「解析失败」其实和解析器无关,而是文件本身:Windows 记事本保存的 UTF-8 带 BOM、JSON 含 \uXXXX 转义但没声明 encoding、或者用 fopen 以文本模式打开二进制流导致换行符错乱。
建议动作:
- 先用
file -i big.json或 Pythonchardet确认真实编码,强制转成 UTF-8 再处理 - 用
mmap替代ifstream可进一步减少系统调用开销,但simdjson::ondemand::parser不直接支持 mmap —— 需包装成simdjson::padded_string或用simdjson::dom::parser(此时会全量加载,慎用) - 若 JSON 是分行格式(ndjson),别硬当单个 JSON 解析 —— 用
std::getline每行喂给simdjson::padded_string::from_file(),更稳
真正卡住人的,往往不是解析逻辑,而是 fopen 模式选错、BOM 没跳过、或把 10MB 的单行 JSON 当作 ndjson 处理 —— 这些细节不检查,换十个库都一样崩。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











