C++追加写入JSON文件时如何删除倒数第二行的]保证格式合法
C++ 追加写入合法JSON文件的实现方案
靠文件追加模式、手动删除倒数第二行]的写法容错率极低,只要文件末尾多了空行、空格,定位逻辑就会失效,最终输出格式错误的JSON。下面是两种稳定可落地的实现方案,完全不需要依赖不可靠的行号定位:
方案1:中小数据量场景(单文件大小<100MB)
这是最不容易出错的实现,逻辑非常简单:
- 初始化时如果文件不存在,直接写入合法的基础JSON结构
- 每次写入新数据前,把整个文件的内容完整读入内存
- 在内存中完成字符串拼接:定位到数组末尾的
],截断后插入新的条目内容,再补全闭合的括号 - 把拼接完成的完整内容覆盖写回原文件
这种写法完全不需要处理复杂的文件流随机读写,哪怕文件末尾有多余空白字符也能正常处理,给你一份可直接运行的参考代码:
#include <fstream> #include <string> #include <sstream> #include <cctype> struct IOEntry { int tag; int disp; std::string mnemonic; bool iotype; }; // 对应你原来的makefile()初始化逻辑 void initJsonFile() { std::ifstream checkFile("data.json"); if (!checkFile.good()) { std::ofstream outFile("data.json"); outFile << R"({ "IO Inputs": [] })"; outFile.close(); } checkFile.close(); } // 对应你原来的write()写入逻辑 void writeEntry(const IOEntry& entry) { initJsonFile(); // 读入全量文件内容 std::ifstream inFile("data.json"); std::stringstream buffer; buffer << inFile.rdbuf(); std::string content = buffer.str(); inFile.close(); // 定位数组结束符]的位置 size_t arrEndPos = content.find_last_of(']'); if (arrEndPos == std::string::npos) { // 文件格式损坏,重置为初始结构 std::remove("data.json"); initJsonFile(); return; } // 判断数组是否为空,空数组不需要加前置逗号 bool isEmptyArray = true; for (size_t i = arrEndPos - 1; i > 0; --i) { if (content[i] == '[') break; if (!isspace(static_cast<unsigned char>(content[i]))) { isEmptyArray = false; break; } } // 截断到]之前的位置,拼接新条目 content.erase(arrEndPos); if (!isEmptyArray) { content += ",\n"; } content += " {\n"; content += " \"tag\": " + std::to_string(entry.tag) + ",\n"; content += " \"disp\": " + std::to_string(entry.disp) + ",\n"; content += " \"mnemonic\": \"" + entry.mnemonic + "\",\n"; content += " \"iotype\": " + (entry.iotype ? "true" : "false") + "\n"; content += " }\n"; // 补全闭合括号 content += "]\n}"; // 全量覆盖写回 std::ofstream outFile("data.json"); outFile << content; outFile.close(); }
注意:手动拼接JSON字符串时,一定要对用户输入内容中的双引号、反斜杠、换行符等特殊字符做转义处理,否则依然可能出现格式错误。
方案2:大数据量场景(单文件大小>100MB,不适合全量读入内存)
如果数据量太大,全量读写的性能无法接受,可以直接操作文件流的读写指针定位,核心逻辑是不要按行找],而是从文件末尾往前逐字符跳过所有空白字符,定位到最后一个非空白的]字符,把写指针移到这个位置直接覆盖即可,核心定位代码片段如下:
#include <fstream> #include <cctype> // 打开文件支持同时读写 std::fstream file("data.json", std::ios::in | std::ios::out); file.seekg(0, std::ios::end); long long fileSize = file.tellg(); long long bracketPos = -1; char curChar; // 从文件末尾往前遍历,找第一个非空白字符 for (long long i = fileSize - 1; i >= 0; --i) { file.seekg(i); file.get(curChar); if (!isspace(static_cast<unsigned char>(curChar))) { if (curChar == ']') { bracketPos = i; } break; } } if (bracketPos == -1) { // 此处处理文件格式损坏的情况 file.close(); return; } // 将写指针移到]的位置,后续写入新条目后补全]\n}即可 file.seekp(bracketPos); // 此处追加逗号、新条目内容,最后写入闭合括号 // ... 你的写入逻辑 file.close();
实用建议:如果是正式项目使用,不要手动拼接JSON,直接接入成熟的JSON序列化库即可,库会自动处理格式校验、特殊字符转义、结构闭合等问题,能避免90%以上的JSON格式错误。
内容的提问来源于stack exchange,提问作者Dan
相关产品推荐
相关产品推荐

