资讯动态

VC6.0集成jsoncpp解决中文乱码:编译配置与GBK/UTF-8转换全案例

发布时间:2026/10/20 4:01:54 来源:尧图企业网站定制
简介面向Visual C 6.0开发者的JSONCPP集成与中文乱码处理完整案例包以jsoncpp-src-0.5.0为基础解决VC6.0下解析、生成含中文JSON数据时的编码异常问题。压缩包共72个文件以头文件24个h、源文件9个cpp、内联模板6个inl及编译中间文件为主并附带2份doc使用说明和3份txt笔记整体仅3.77MB结构清晰便于按工程目录对照学习。已有590人学习浏览适合需要在老旧VC6.0环境中快速接入JSONCPP的C初学者或维护遗留项目的工程师。压缩包内含可直接运行的测试工程、关键源码类json_reader/json_writer/json_value及专门编写的防乱码配置说明可让读者在对话框或控制台程序中完成JSON输入、解析、序列化与中文显示验证免去自行摸索编码转换与工程配置的麻烦。1. VC6.0 还在跑生产代码这套 jsoncpp 全案例专治中文乱码某做工业控制的朋友被一个维护了近二十年的上位机工程困住了VC6.0 的老 IDE 换不得新业务却要求对接返回 JSON 的 HTTP 接口报文里全是中文参数。他把 jsoncpp 源码塞进工程编译一把过运行时中文全乱。问题不在编译在编码链jsoncpp 内部约定用 UTF-8 与外界交换文本而 VC6.0 的程序默认按 GBK 解释字符串中间少了转换层乱码就是必然的。这套「VC6.0 调用 jsoncpp 全案例」把解析字符串、读取配置文件、生成请求报文、中文防乱码这些场景全写成可编译运行的示例代码属于已经踩完坑的无错版本。适合被老工程绑住手脚的人也适合课程设计里被要求「必须用 VC6.0」的学生——它不教理论直接给你能拖进工程跑的东西。2. 工程解剖与编译配置把 jsoncpp 源码顺利塞进 VC6.02.1 目录结构一个工作区、一组头文件、三个源文件jsoncpp 的优点是不依赖任何第三方库可以当成普通源码直接编进工程这对老系统特别友好。整个库对外只有一个入口头文件json/json.h实现部分就三个 cpp加上几个辅助头文件。案例包把 VC6 的工程文件也一并准备好了目录大致长这样jsoncpp_demo/ │ demo_parse.dsp / demo_parse.dsw // 解析示例的工程与工作区 │ demo_generate.dsp / demo_generate.dsw ├─ jsoncpp/ │ ├─ include/ │ │ └─ json/ │ │ ├─ json.h // 对外统一入口业务代码只 include 它 │ │ ├─ config.h // 平台相关宏定义 │ │ ├─ features.h // 编译特性开关 │ │ ├─ forwards.h // 前置声明 │ │ ├─ reader.h // 解析器 Reader 声明 │ │ ├─ value.h // 核心类型 Value │ │ └─ writer.h // Writer / FastWriter / StyledWriter │ └─ src/ │ ├─ json_reader.cpp // Reader 解析实现 │ ├─ json_value.cpp // Value 存储与操作实现 │ └─ json_writer.cpp // JSON 序列化实现 └─ samples/ ├─ config.json // 带 BOM 的 UTF-8 配置文件样例 └─ resp.json // 接口返回报文样例拿到这份目录第一件事应该是打开json.h确认它 include 的是reader.h、value.h、writer.h这套老接口而不是CharReaderBuilder那套新 API。新版 jsoncpp 已经要求 C11源码里大量出现nullptr、auto、右值引用VC6.0 编译器根本不认识这些关键字在头文件阶段就会编译失败。所以包内带的是老版本源码所有示例代码也只使用老 API二次开发时务必保持这个约定别去网上抄新写法。2.2 编译前必须核对的三处工程设置VC6.0 的工程选项在 Project → SettingsAltF7里分 C/C 页和 Link 页。把 jsoncpp 加进工程后先别急着点 Build按下面的表逐项核对配置项推荐值说明Use run-time libraryRelease 用 Multithreaded DLL/MDDebug 用 Debug Multithreaded DLL/MDd必须与主工程完全一致否则跨模块内存释放直接崩Preprocessor definitions保留_MBCS去掉_UNICODE与UNICODE本方案走窄字符加 GBK 转换通道不碰宽字符Additional include directoriesjsoncpp\include的完整路径用绝对路径或$(ProjectDir)前缀别用裸相对路径Warning levelLevel 3VC6 的 Level 4 误报太多会淹没真正的错误Runtime Library 是这里最值得强调的坑。很多从网上下载的 lib 用 /MT 编译而委托项目的主程序用 /MD两者各持一份 CRTnew在 lib 里分配、delete在主程序里释放堆校验直接失败。最典型的表现是 Debug 版好端端Release 版一启动就非法访问。案例包里推荐的接法是直接把三个 cpp 编进主工程从源头消灭两套 CRT 的问题。_UNICODE是另一个隐形地雷。从别处拷贝的工程经常带着这个宏一旦定义了Win32 API 全部走宽字符版本代码里所有char*传进去就是 C2664 编译错误而 jsoncpp 的 API 是窄字符的混用还得来回转 UTF-16得不偿失。所以案例包统一采用_MBCS模式中文全部交给后文讲的转换函数处理。头文件 include 之前建议先压一压警告否则 VC6 下 jsoncpp 头文件会刷出几百条无关警告真正报错的地方反而被淹没// StdAfx.h 或主头文件最上方include json 之前 #pragma warning(disable: 4018 4100 4127 4189 4244 4251 4267 4275) #include json/json.h这里 4251 和 4275 是模板类导出警告jsoncpp 的Value在 VC6 下导出时几乎必触发4244、4267 是类型转换精度4018 是有符号无符号比较。它们都不影响运行结果只在编译输出里制造噪音。加了这段之后整个 jsoncpp 的编译输出能干净到只剩个位数警告。2.3 三种接入方式直接编源码、预编 lib还是绕路接入方式按维护成本排序第一种最省心把json_reader.cpp、json_value.cpp、json_writer.cpp三个文件直接 Add to Project和主程序一起编译。jsoncpp 代码量不大编译时间可以接受这是案例包默认方式。单独建一个 .dsp 生成jsoncpp.lib放到 ThirdParty 目录。这样主工程干净但前提是主工程和 lib 的 Runtime Library 必须一致否则会踩 2.2 节的雷。把 jsoncpp 编成 DLL。老版本官方没提供好用的导出宏配置Value、std::string跨 DLL 传递符号多得吓人非必要不选这条路。如果编译时遇到 LNK2005 重复符号说明你已经以 lib 方式引入过一次又加了源码二选一即可别重复。还有一个小提醒同事拷给你的 .dsp 里include 绝对路径往往指向他那台机器的目录打开工程后第一件事是重新指定 jsoncpp 的 include 路径否则头文件找不到。这条看着弱智实际踩到的人真不少。提示案例包里 demo 工程默认已经配好了上述设置但导入你自己的工程时请逐项对着 2.2 的表重新核对别相信「默认」。3. 核心实现拆解GBK 与 UTF-8 之间的双向转换通道3.1 乱码根因三个字节流环节要理解这套代码为什么防乱码先看清一条 JSON 数据在 VC6.0 程序里经过的三个环节。第一环是源代码文件本身VC6.0 时代的工程普遍把 .cpp 保存为 ANSI 编码中文 Windows 上就是 GBK编译器直接把字面量按 GBK 字节写进代码段。第二环是 jsoncpp它的Value内部不做编码探测你塞进什么字节就存什么字节但对外读写统一按 UTF-8 约定处理。第三环是显示层MessageBox、编辑框、printf都按系统 ANSI 代码页解释字节中文系统默认 936 即 GBK。所以乱码的本质是同一份字节流在三个环节里被不同编码解释。从接口收进来的 UTF-8 报文不转换直接丢给 GBK 的界面控件乱反过来用户在界面输入的中文是 GBK 字节不转换直接塞进Value再发给接口对方按 UTF-8 解析也乱。防乱码方案不是改 jsoncpp而是在它和外界之间架两条转换通道进库前 GBK 转 UTF-8出库后 UTF-8 转 GBK。3.2 两条转换函数GBKToUTF8 与 UTF8ToGBK转换用 Windows 系统 API 做稳定且不引入额外依赖。核心是两段式调用第一次传 NULL 拿缓冲区长度第二次真正转换。#include windows.h #include string #include vector // GBK(ANSI) 字符串 - UTF-8 std::string GBKToUTF8(const std::string strGBK) { int nWLen MultiByteToWideChar(CP_ACP, 0, strGBK.c_str(), -1, NULL, 0); if (nWLen 0) return ; std::vectorwchar_t wbuf(nWLen); MultiByteToWideChar(CP_ACP, 0, strGBK.c_str(), -1, wbuf[0], nWLen); int nULen WideCharToMultiByte(CP_UTF8, 0, wbuf[0], -1, NULL, 0, NULL, NULL); std::vectorchar ubuf(nULen); WideCharToMultiByte(CP_UTF8, 0, wbuf[0], -1, ubuf[0], nULen, NULL, NULL); return std::string(ubuf[0]); } // UTF-8 - GBK(ANSI)用于把 jsoncpp 取出的值显示到控件上 std::string UTF8ToGBK(const std::string strUTF8) { int nWLen MultiByteToWideChar(CP_UTF8, 0, strUTF8.c_str(), -1, NULL, 0); if (nWLen 0) return ; std::vectorwchar_t wbuf(nWLen); MultiByteToWideChar(CP_UTF8, 0, strUTF8.c_str(), -1, wbuf[0], nWLen); int nGLen WideCharToMultiByte(CP_ACP, 0, wbuf[0], -1, NULL, 0, NULL, NULL); std::vectorchar gbuf(nGLen); WideCharToMultiByte(CP_ACP, 0, wbuf[0], -1, gbuf[0], nGLen, NULL, NULL); return std::string(gbuf[0]); }几个参数说明CP_ACP表示系统当前 ANSI 代码页中文 Windows 默认 936GBK所以这两个函数名字里的 GBK 是一个约定代码页由系统决定CP_UTF8指定目标编码。第二个参数dwFlags传 0表示不做特殊字符映射。-1表示输入是按\0结尾的 C 风格字符串转换时连结束符一起处理。返回值里std::string(ubuf[0])依赖缓冲区末尾的\0截断所以长度参数必须用包含结束符的长度。我特意用std::vectorchar做中间缓冲而不是手工new[]/delete[]是为了在 VC6 下减少内存管理的出错点。如果遇到老代码风格要求换成 new/delete 完全等价但 VC6 没有std::unique_ptr手工版本漏掉一个delete[]就是泄漏vector 版本不用操心这些。3.3 解析接口返回的中文怎么取值解析的完整套路是原始报文GBK 或 UTF-8 视来源而定统一转成 UTF-8 后交给Reader::parse取出来的字符串再按需转回 GBK 显示。以最常见的一段代码为例// 假设 strBody 是从 Socket/文件读到的原始字节中文 Windows 下按 GBK 处理 std::string strUTF8 GBKToUTF8(strBody); Json::Value root; Json::Reader reader; if (!reader.parse(strUTF8, root)) { // 出错时不要只看返回值先打错误信息 OutputDebugString(reader.getFormattedErrorMessages().c_str()); return; } std::string strName root[name].asString(); // 这里拿到的是 UTF-8 字节 std::string strShow UTF8ToGBK(strName); // 显示给界面看再转一次Reader::parse有两个细节值得记一下一是它接受const std::string所以传进去的 strUTF8 生命周期要覆盖 parse 调用二是失败时getFormattedErrorMessages()会给出行号和列号这在排查对端报文格式问题时比猜快得多。root[name]在 key 不存在时不会崩溃jsoncpp 会返回一个空Value对它调asString()得到空串这是它比手写解析器舒服的地方。多层取值root[data][token]同样安全但要警惕一个隐性开销operator[]在 key 不存在时会创建空Value留在内存里如果报文里有大量可选字段且每次都直接取内存会缓慢增长。稳妥的做法是先root.isMember(name)判断再取特别是对弈接口版本升级、字段消失的情况。3.4 生成从界面输入构造接口报文构造报文的对称操作是界面拿到的字符串是 GBK塞进Value前先转 UTF-8序列化出来的字节流直接发给接口不要画蛇添足再转一次。std::string MakeJsonRequest(const std::string strClientName) { Json::Value root; root[op] login; root[client_name] Json::Value(GBKToUTF8(strClientName)); Json::Value ext; ext[platform] Json::Value(GBKToUTF8(工控机)); ext[channel] 1; root[ext] ext; Json::FastWriter writer; return writer.write(root); // 返回 UTF-8 字节流直接用于发送 }这里最容易犯的错有两个。第一C 源文件里的字符串字面量「工控机」如果 .cpp 保存为 ANSI那它在内存里就是 GBK 字节直接赋给Value会让对方按 UTF-8 解析时看到乱码所以字面量也要过GBKToUTF8。第二FastWriter::write返回的已经是 UTF-8发送给网络接口的包不要再转回 GBK什么时候才转回 GBK只有显示到控件、写进本地方日志这两类场景。把「网络与文件用 UTF-8界面与日志用 GBK」这个边界定死整套代码的编码思路就清晰了。4. 全案例串讲字符串、配置文件与嵌套报文的调用姿势4.1 案例一Socket 收包后的登录响应解析工控上位机最常见的场景是 TCP 收包后解析 JSON。假设协议是 4 字节长度头加 JSON 体收包处理的函数可以这样组织BOOL OnRecvLoginResponse(const char* pPacket, int nLen) { // 跳过 4 字节长度头 const char* pBody pPacket 4; int nBodyLen nLen - 4; std::string strBody(pBody, nBodyLen); std::string strUTF8 GBKToUTF8(strBody); Json::Value root; Json::Reader reader; if (!reader.parse(strUTF8, root)) { OutputDebugString(reader.getFormattedErrorMessages().c_str()); return FALSE; } int nCode root[code].asInt(); if (nCode ! 0) return FALSE; // 业务层错误按你的协议处理 std::string strToken UTF8ToGBK(root[data][token].asString()); m_strToken strToken; // 存 GBK 版下次发请求前再转 UTF-8 return TRUE; }这个例子的关键决策是 m_strToken 存 GBK。因为后续拼请求头时需要把 token 放进另一个 JSON 报文的字段里发送前统一过一次GBKToUTF8而中间如果要在调试窗口打印 tokenGBK 直接可读。全链路里每个字符串只转两次不多不少这是防乱码的好习惯。4.2 案例二读取带 BOM 的 UTF-8 配置文件很多配置 json 是用记事本存成 UTF-8 的记事本会在文件头塞三个字节的 BOMEF BB BF。jsoncpp 的 Reader 不认 BOM直接把 BOM 当第一个字符解析结果必然是 parse 失败。案例包里给的读取函数先用二进制方式读文件再手工剥离 BOMbool LoadConfig(const char* szPath, Json::Value root) { FILE* fp fopen(szPath, rb); if (!fp) return false; fseek(fp, 0, SEEK_END); long nSize ftell(fp); fseek(fp, 0, SEEK_SET); std::vectorchar buf(nSize 1, 0); fread(buf[0], 1, nSize, fp); fclose(fp); std::string content(buf[0]); // 去掉 UTF-8 BOMEF BB BF if (content.size() 3 (unsigned char)content[0] 0xEF (unsigned char)content[1] 0xBB (unsigned char)content[2] 0xBF) { content content.substr(3); } Json::Reader reader; return reader.parse(content, root); }为什么用fopen而不是ifstreamVC6 的 STL 流对中文路径的支持非常弱文件路径里带中文目录时经常无声失败fopen走 CRT 的 ANSI 路径反而稳定。std::vectorchar在这里充当一次性缓冲避免手工释放。BOM 剥离后文件内容如果本身是 UTF-8直接 parse如果配置文件被人用 GBK 存过解析前需要再过一次GBKToUTF8所以完整方案里读配置的流程是自动判断文件编码后分派文件实际编码处理方式UTF-8 无 BOM直接 parseUTF-8 带 BOM去掉 EF BB BF 后 parseANSI / GBKGBKToUTF8后 parseUTF-16先转 UTF-8不建议直接用4.3 案例三构造嵌套对象与数组的上报报文上报传感器数据这类需求JSON 里既有数组又有嵌套对象构造顺序是先建子对象再挂到根上。注意数组里的中文字符串同样要过GBKToUTF8std::string BuildReport() { Json::Value root; root[type] sensor_report; root[count] 2; Json::Value arr; arr.append(Json::Value(GBKToUTF8(温度))); arr.append(Json::Value(GBKToUTF8(湿度))); root[channels] arr; std::string strNote GBKToUTF8(他说\正常\); root[note] Json::Value(strNote); Json::FastWriter writer; return writer.write(root); }append会自动适配数组类型不需要预先告诉 Value 它是数组字符串里的双引号由${jsoncpp}负责转义成\不用自己处理。读取数组的对称写法是const Json::Value arr root[channels]; for (int i 0; i (int)arr.size(); i) { std::string strCh UTF8ToGBK(arr[i].asString()); // strCh 是 GBK 的 温度/湿度可以直接塞进 ListBox }这里强制把arr.size()转成int是因为 VC6 下size_t与int比较会触发 C4018 警告与其压制不如显式转换。遍历数组时用引用const Json::Value避免每次arr[i]拷贝整个子节点老编译器下这个大对象拷贝的开销相当可观。5. 避坑记录五个让 jsoncpp 在 VC6.0 上翻车的现场下面五条是我自己在维护老工程和帮同事救火时踩过的坑按「现象 → 原因 → 解决」列出来每一条背后都对应一次真实的排错过程。5.1 中文变成 \u5f20\u4e09 转义串现象用FastWriter生成的 JSON 里中文全变成\u5f20\u4e09这类转义序列粘贴到接口文档里能看懂但作为文件发给一个只做字符串匹配的对端程序对方直接不认。原因不同小版本的 jsoncpp 对非 ASCII 字符的写回策略不同。有的版本直接把 UTF-8 字节写进输出流有的版本为了避免编码问题统一转成\uXXXX。两种写法都是合法 JSON任何标准解析器都能还原但如果对端程序没有走 JSON 解析而是拿字段值去拼字符串就会翻车。解决先确认对端到底怎么消费 JSON。如果对端需要的是 UTF-8 字节写一个小函数把转义序列展开回 UTF-8 字节流static int HexToNum(char c) { if (c 0 c 9) return c - 0; if (c a c f) return c - a 10; if (c A c F) return c - A 10; return 0; } std::string UnescapeJsonUnicode(const std::string in) { std::string out; for (size_t i 0; i in.size();) { if (in[i] \\ i 5 in.size() in[i 1] u) { unsigned int code 0; for (int j 0; j 4; j) code (code 4) HexToNum(in[i 2 j]); if (code 0x80) out (char)code; else if (code 0x800) { out (char)(0xC0 | (code 6)); out (char)(0x80 | (code 0x3F)); } else { out (char)(0xE0 | (code 12)); out (char)(0x80 | ((code 6) 0x3F)); out (char)(0x80 | (code 0x3F)); } i 6; } else { out in[i]; i; } } return out; }这个展开逻辑只处理基本平面内的码点遇到\ud83d这类代理对码元需要成对合并工控场景基本不涉及所以案例包没做那部分。对\uXXXX展开成 UTF-8 的字节组装规则是固定套路三字节序列的前导字节用0xE0 | (code 12)两个续字节分别取中间 6 位和低 6 位。5.2 parse 一直返回 false错误信息却是空现象JSON 字符串眼瞅着没问题reader.parse就是返回 falsegetFormattedErrorMessages()打出来一行「No error」让人摸不着头脑。原因字符串里混了 BOM 或者根本是 UTF-16 编码。老版本 jsoncpp 的 Reader 从字节流头部开始解析EF BB BF三个字节被当成非法字符直接判定失败。如果报文来自某些老设备接口的 UTF-16 输出中间还夹杂\0字符std::string的长度计算也会连带出错。解决parse 之前统一做一次字节流清洗先剥 BOM 再转换bool StripBOM(std::string s) { if (s.size() 3 (unsigned char)s[0] 0xEF (unsigned char)s[1] 0xBB (unsigned char)s[2] 0xBF) { s s.substr(3); return true; } return false; }剥完 BOM 如果字符串里还带\0说明源是 UTF-16别硬解先让对端改输出 UTF-8 更省事。这个坑的教训是parse 失败不一定是 JSON 语法错先检查字节流头三个字节是不是 BOM。5.3 编译报错 C2065 nullptr / C2664 无法转换参数现象从网上新示例里抄来的代码粘进 VC6.0编译报C2065: nullptr: undeclared identifier或者auto关键字的语法错误刷屏。原因VC6.0 的编译器停留在 C98 语法的水准上不认识nullptr、auto、右值引用这些后面才进入标准的特性。新版 jsoncpp 的头文件里已经大量使用这些特性整个库都编译不过更别提业务代码了。解决坚持用老版本源码和老 API。网上看到的「新写法」比如Json::CharReaderBuilder、Json::StreamWriterBuilder全部不碰只用Reader/Value/FastWriter这三个老面孔。有人会在头文件顶部补一层#define nullptr 0来骗过编译器这只能应付最浅的报错遇到模板推导照样崩不是长久之计。5.4 Release 版一调用就崩Debug 版正常现象调试版跑了一整天没问题切到 Release 配置后程序一进Value析构就非法访问或者把 jsoncpp 编成 lib 给主工程用启动即崩。原因Runtime Library 不一致。lib 用 /MT 编译主工程用 /MD两套 CRT 各自维护自己的堆new在一个堆里分配、delete去另一个堆释放校验直接失败。这是老工程引入第三方库最常见的崩溃来源跟 jsoncpp 本身无关。解决把三个 cpp 直接加进主工程编译从源头保证同一套 CRT如果坚持用 lib就把主工程所有模块的 Runtime Library 配置截图对比一个都别漏。排查时先看崩溃调用栈如果栈停在free()或operator delete附近十有八九就是这个问题。5.5 界面显示正常写进文件再打开全是乱码现象MessageBox弹出的中文正常但同一个字符串用CFile写进 txt 再打开满屏乱码反过来文件里读出来正常放回编辑框又乱。原因没有统一的编码边界。界面控件用 GBK文件与网络字节流约定 UTF-8代码里同一份 string 在两处直接复用等于把 GBK 字节当 UTF-8 写进了文件自然乱。解决把「进入 jsoncpp 之前一律GBKToUTF8出了 jsoncpp 显示一律UTF8ToGBK」当成代码纪律来执行。为了尽早发现漏网之鱼我在调试版加了一个 UTF-8 合法性校验函数在关键路径上断言bool IsValidUTF8(const std::string s) { size_t i 0; while (i s.size()) { unsigned char c (unsigned char)s[i]; if (c 0x80) { i; } else if ((c 0xE0) 0xC0) { if (i 1 s.size()) return false; if (((unsigned char)s[i1] 0xC0) ! 0x80) return false; i 2; } else if ((c 0xF0) 0xE0) { if (i 2 s.size()) return false; if (((unsigned char)s[i1] 0xC0) ! 0x80) return false; if (((unsigned char)s[i2] 0xC0) ! 0x80) return false; i 3; } else return false; } return true; }用法是从 jsoncpp 取到字符串后调用assert(IsValidUTF8(strName))如果这里断言失败说明有人绕过了转换层直接传了 GBK 进来能精确定位到漏转换的那一行。注意这是必要非充分的检查——个别 GBK 字节序列碰巧也是合法 UTF-8所以它只用来抓低级错误真正的保障还是编码纪律本身。6. 二次开发封装自己的 JsonTool 与三样本回归6.1 三个封装决策第一层封装建议写一个JsonTool类把Reader、Value、转换函数全部收进去业务代码只跟string打交道class JsonTool { public: bool Parse(const std::string strGBK) { std::string strUTF8 GBKToUTF8(strGBK); return m_reader.parse(strUTF8, m_root); } // 路径语法自定义data/token按 / 逐级下沉 std::string GetString(const std::string strPath, const std::string strDefault) const { Json::Value node m_root; size_t nPos 0; while (nPos strPath.size()) { size_t nSlash strPath.find(/, nPos); std::string strKey (nSlash std::string::npos) ? strPath.substr(nPos) : strPath.substr(nPos, nSlash - nPos); if (!node.isObject() || !node.isMember(strKey)) return strDefault; node node[strKey]; if (nSlash std::string::npos) break; nPos nSlash 1; } return node.asString(); } int GetInt(const std::string strPath, int nDefault) const; private: Json::Value m_root; Json::Reader m_reader; };三个决策说明路径语法是自己定义的不是 JSON Pointer用前要跟团队成员讲清楚GetString返回前不转 GBK因为调用方可能拿它拼报文需要 UTF-8统一返回 UTF-8 让调用方决定VC6 下模板和 lambda 都受限GetString/GetInt老老实实写成重载成员函数别折腾泛型。6.2 三样本回归换机器前必须跑一遍封装完成后我习惯在main()里挂一组最小回归用例每次把代码挪到新工程时先跑样本处理链预期结果纯 ASCII 的 JSON 字符串直接 parse字段值与预期一致GBK 字面量构造报文GBKToUTF8→ 生成输出为合法 UTF-8 字节流UTF-8 带 BOM 的配置文件去 BOM → parse中文 key 能取到正确值回归代码很简单第一个样本断言tool.GetString(a, ) value第二个样本用IsValidUTF8校验序列化结果第三个样本走LoadConfig后取中文 key 与期望串比对。三个样本全过基本可以放心交付。这套案例和JsonTool的完整源码我都整理在资源包里了下载后按第 2 章的配置走一遍就能编译。另外补一个换机器时的高频隐患如果目标运行环境的 Windows 是英文版CP_ACP就变成 1252 而不是 936GBKToUTF8名字没变行为却会把中文字节解析成拉丁字符产生乱码。从那以后我每次把 jsoncpp 引入旧工程都会强制走一遍「确认目标机代码页 → 核对 Runtime Library → 跑三个样本回归」三步乱码和崩溃基本没再复发。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑