ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

VC6.0下jsoncpp配置与中文乱码处理:编译选项与编码转换实践

VC6.0下jsoncpp配置与中文乱码处理:编译选项与编码转换实践 简介面向 VC6.0 开发者的 JSONCPP 集成资源针对在 Visual C 6.0 环境下解析、生成 JSON 时常见的中文乱码问题提供了完整的案例工程与可直接运行的程序。压缩包共 72 个文件整体 3.77MB以头文件h、源码cpp、内联模板inl为核心附带工程文件dsp/dsw、界面资源rc/ico以及编译辅助文件obj/sbr并包含一份【重要】使用说明文档和必读文本。已有 590 人学习下载资源内置 jsoncpp-src-0.5.0 源码无需另行编译库文件加入工程即可使用。具体内容包括完整的 VC6.0 对话框/控制台测试案例、中文 UTF-8 编码处理配置、JSON 解析与序列化的可运行演示以及排错思路。通过使用说明文档可快速掌握在 VC6.0 中规避中文乱码、稳定处理 JSON 数据的方法对维护旧系统或学习早期 C 项目集成的开发者尤为实用。1. 给 VC6.0 补上 JSON 解析能力为什么首选 jsoncpp早该被淘汰的 VC6.0 依然在工控、教学和存量维护项目里大量存在而 JSON 已经成为设备接口和配置文件的默认交换格式。在 VC6.0 上解析 JSON 有个天然痛点这个古董编译器只支持 C98 的早期子集很多现代 C 库根本编不过更不用说 STL 的 wchar_t 支持和编码处理在 VC6 上表现得极其别扭。jsoncpp 是少有的、能在这个环境下稳定编译的 C JSON 库而且它开源、单包可移植、API 足够直观适合嵌入式设备调试工具和 Windows 老项目里做配置读写。这份资源的价值在于把 jsoncpp 的源码包、工程配置、各种解析和生成案例全部打包好并且专门对中文乱码做了预处理拿到手不用再去网上东拼西凑找兼容补丁适合做设备联调、写上位机工具、维护老系统的从业者。2. 搭建 VC6.0 的 jsoncpp 环境头文件、静态库与工程选项2.1 编译前的准备源码里挑对文件别一股脑全编进工程jsoncpp 的源码从 GitHub 上下下来是一整个仓库但 VC6.0 的工程不需要那么多文件。我一般只挑src/lib_json目录下的json_reader.cpp、json_writer.cpp、json_value.cpp三个实现文件以及include目录下的json/头文件目录。其他如json_tool.h、json_batchallocator.h是这三个文件内部依赖的辅助头不需要额外加入工程编译时自动带上。很多人在这一步翻车的原因是直接把整个src目录拉进工程结果json_reader.cpp里引用了json_batchallocator.h而 VC6 的预处理器对当前文件所在目录的搜索顺序和现代 IDE 不一样经常报“无法打开包含文件”的错误。解决办法是把include目录在“工具 - 选项 - 目录 - Include files”里加到最前面同时把src/lib_json目录也加进包含路径这样三个 cpp 文件无论放在工程的哪个子目录都能正确找到头文件。// 在工程设置里添加包含路径后头文件这样引用即可 #include json/json.h #include iostream #include fstream这段代码的头文件引用方式决定了你的工程目录结构json/json.h意味着你的包含路径必须指向include目录这样编译器会去include/json/下找json.h。我见过有人把json.h直接拷到工程根目录再改成#include json.h也能编过但后续升级源码包时容易漏掉其他头文件不建议这么干。如果用的是源码包里的json/json.h那json/value.h、json/reader.h、json/writer.h、json/features.h这些也会被自动递归包含不需要手动逐个添加。2.2 编译选项的四个关键点C 语言标准、warning 级别和预处理指令VC6.0 对 C 标准的支持停留在 1998 年之前的水准比 C98 还要老。jsoncpp 官方源码为了兼容旧编译器在json/config.h里做了一些预处理判断但 VC6 的_MSC_VER值对应的是 1200新版 jsoncpp 的某些分支没有覆盖到这个老版本所以需要在工程里强制定义两个宏来解决。// 在工程设置 - C/C - Preprocessor definitions 里添加 // _WIN32 // _MSC_VER1200 // JSON_NO_INT64 (如果不需要 64 位整数解析)第一个宏_WIN32是确认 Windows 平台jsoncpp 内部有一些平台相关的分支没有这个宏会走 Linux 分支导致std::string的迭代器类型不匹配。_MSC_VER1200是让编译器报告自己的版本号jsoncpp 的config.h里有针对_MSC_VER的#if _MSC_VER 1400之类的判断会启用一些旧代码路径比如禁用long long的格式化输出。JSON_NO_INT64是可选宏老工程不需要解析超过 32 位整数的 JSON 时建议加上防止Json::Value内部使用json_int_t类型在 VC6 下出现__int64与long long混用问题。Warning 级别我建议设置在Level 1也就是只显示严重警告。jsoncpp 的源码本身能通过 Level 1 编译但如果有第三方库一起编译Level 4 的警告可能刷屏。最烦人的是 VC6 对某些for循环作用域的处理和标准不一致jsoncpp 源码里已经尽量避免但你真的遇到for loop initial declaration used outside C99 mode之类的提示需要在编译选项里把Disable Language Extensions关掉也就是不要勾选“禁用语言扩展”。2.3 编译成静态库还是直接用源码两种方式的效率对比在 VC6.0 里使用 jsoncpp 有两种姿势把三个 cpp 文件直接加进自己的工程一起编译或者先编译一个json_lib.lib静态库再用。直接加源码的方式最简单但每次编译整个工程都要重新编译 jsoncpp 的三个文件工程稍微大一点就浪费时间。编静态库的方式一劳永逸但初次配置稍微繁琐。静态库工程的配置要点新建一个 Win32 Static Library 工程把三个 cpp 文件加进去编译选项和 2.2 节完全一致然后编译生成.lib文件。在使用端工程的“链接器 - 输入 - 附加依赖项”里填入这个 lib 的名字和路径。需要注意VC6 的静态库有单线程版和多线程版之分如果你的使用端工程用的是 Debug Multithreaded那静态库也必须用相同的运行时库选项编译否则链接时会报LNK2005或LNK4098这种符号冲突错误。# 链接器报错示例LNK2005 _malloc already defined in LIBCMT.lib # 原因静态库用单线程 CRT(/ML) 编译而使用端用多线程 CRT(/MT) # 解决在静态库工程设置里把 Runtime library 改成和使用端一致的选项我一般习惯直接编静态库特别是在维护多个老项目的时候一份编译好的 lib 可以给三四个工程共用。但如果你是第一次调通 jsoncpp我建议先用直接加源码的方式跑通代码逻辑等确认没问题再抽成静态库否则一次出现环境问题和代码问题叠加在一起排错会非常痛苦。3. 基础解析案例从单层对象到嵌套数组的实际用法3.1 读一个简单 JSON 对象parse 的三种方式与返回值处理jsoncpp 的Reader类提供三种 parse 重载形式接收 C 字符串、接收std::string、接收std::istream。在 VC6.0 里std::istream重载形式用得最多因为设备接口经常把 JSON 数据放在文件流或串口数据流里。无论哪种重载第一个参数是 JSON 数据源第二个参数是接收结果的Json::Value引用返回bool表示解析是否成功。#include json/json.h #include iostream #include fstream bool parseJsonFile(const char* filePath, Json::Value root) { std::ifstream fin(filePath); if (!fin.is_open()) { std::cerr Cannot open file: filePath std::endl; return false; } Json::Reader reader; bool success reader.parse(fin, root, false); if (!success) { // 用 collectErrors 拿到具体错误信息 std::string errMsg reader.getFormatedErrorMessages(); std::cerr Parse error: errMsg std::endl; } fin.close(); return success; }这段代码演示了标准的三步流程打开文件流、调用 parse、失败时取错误信息。第三个参数false表示不收集所有错误信息只保留第一条错误这样出错时能快速定位。getFormatedErrorMessages()在解析失败时返回包含行列号的错误描述这个信息在调试设备返回的半截 JSON 数据时特别实用能直接告诉你 JSON 在哪一行哪个位置断了。有一个容易被忽略的点parse 成功后Json::Value对象里的数据如果不及时提取会随root的生命周期失效。所以解析函数尽量不要把Json::Value以值方式返回而是用引用参数传入或者用智能指针管理。VC6 没有std::shared_ptr一般用auto_ptr或者直接传引用的方式我习惯传引用代码更直白也不会在对象拷贝时触发 jsoncpp 内部引用计数的隐患。3.2 取字段值的类型判断isValidMember 与 type 检查配合设备返回的 JSON 字段经常缺失或者类型不对有些人直接root[temperature].asDouble()取完之后才发现数据是字符串类型的23.5asDouble 返回 0排查半天。jsoncpp 的Value::isMember()方法只判断字段是否存在不判断类型对不对。更稳妥的写法是先判断类型再决定怎么取值。bool readTemperature(const Json::Value root, double temp) { const char* fieldName temperature; if (!root.isMember(fieldName)) { std::cerr Field missing: fieldName std::endl; return false; } const Json::Value field root[fieldName]; if (field.isDouble() || field.isInt()) { temp field.asDouble(); return true; } else if (field.isString()) { temp atof(field.asCString()); return true; } std::cerr Field type mismatch: fieldName type field.type() std::endl; return false; }这段代码体现的是防御式解析思路先检查字段存在性再检查类型字符串数字也用atof兜底转换。field.type()返回一个枚举值Json::intValue为 0、uintValue为 1、realValue为 2、stringValue为 3 等等把这些值打印出来排查非常直观。另外注意field.isInt()在 VC6 下对 32 位整数有效如果你开启了JSON_NO_INT64宏那isInt64()方法根本不存在不要去调用否则编译直接报错。3.3 解析嵌套数组数组遍历与对象数组的混合使用解析数组的核心是size()方法和operator[]的整数重载。jsoncpp 的数组可以用for (Json::Value::const_iterator it arr.begin(); it ! arr.end(); it)这种迭代器方式遍历但在 VC6.0 下迭代器的operator*返回Value的引用类型类型推断不如现代 C 灵活容易在赋值时多一次拷贝所以直接用索引遍历更省心。struct SensorPoint { int id; double value; char name[32]; }; bool parseSensorArray(const Json::Value root, SensorPoint* points, int maxCount, int outCount) { if (!root.isMember(sensors) || !root[sensors].isArray()) { std::cerr sensors is not array std::endl; return false; } const Json::Value sensors root[sensors]; outCount 0; int n sensors.size(); if (n maxCount) { std::cerr Array too large: n maxCount std::endl; return false; } for (int i 0; i n; i) { const Json::Value item sensors[i]; if (!item.isObject()) continue; points[i].id item.get(id, -1).asInt(); points[i].value item.get(value, 0.0).asDouble(); strncpy(points[i].name, item.get(name, ).asCString(), 31); points[i].name[31] \0; outCount; } return true; }这里的item.get(id, -1)是 jsoncpp 提供的一个很有价值的方法第二个参数是默认值字段不存在时直接返回默认值不需要像 3.2 节那样先isMember再取。但默认值只保证字段缺失时不崩字段存在但类型不对时get返回的Value还是错的值所以对可靠性要求高的字段建议先用isMember检查。数组遍历的边界条件值得注意sensors.size()返回Json::ArrayIndex类型在 VC6 下是unsigned int和int比较时注意符号问题我写int n sensors.size()时编译器会提示符号转换警告直接强转即可。4. 生成 JSON 数据对象构造、写入文件与格式化控制4.1 用 Value 对象构造嵌套结构Value 的链式赋值原理构造 JSON 输出比解析要友善得多。jsoncpp 的Json::Value重载了operator[]对不存在的字段名会自动创建对应类型的 Value 对象所以可以一层一层地往里塞数据。核心逻辑是先创建Value对象然后用[key]的方式赋值数组用append方法添加元素。Json::Value buildStatusReport() { Json::Value root; root[device_id] DEV-2024-001; root[status] online; root[uptime] 3600; // 嵌套对象直接链式访问子节点 root[network][ip] 192.168.1.100; root[network][mac] AA:BB:CC:DD:EE:FF; // 数组先创建空数组再 append Json::Value sensorList; for (int i 0; i 3; i) { Json::Value item; item[index] i; item[value] i * 1.5; sensorList.append(item); } root[sensors] sensorList; return root; }这里的链式赋值root[network][ip] 192.168.1.100看起来像简单嵌套实际上内部执行了两个步骤第一步root[network]创建一个空的 Value 对象并返回引用第二步在这个引用上再取[ip]并赋值。这种写法的潜在风险是如果你拼错了中间层键名不会报错而是自动创建一个新的空对象最终生成的 JSON 里会出现多余的层级。所以写完构造代码后最好跑一遍流程把生成的 JSON 打印出来人工确认结构。4.2 缩进与换行设置StyledWriter 还是 FastWriterjsoncpp 提供两个 Writer 类FastWriter输出紧凑格式不带多余空格和换行StyledWriter输出带缩进的格式化文本便于人读。设备对接时我一般用FastWriter因为省流量而且很多嵌入式设备解析器对空白字符处理比较弱。写配置文件时用StyledWriter方便人工修改。std::string valueToStyledString(const Json::Value value) { Json::StyledWriter writer; return writer.write(value); } std::string valueToFastString(const Json::Value value) { Json::FastWriter writer; return writer.write(value); }两个 Writer 的write方法都返回std::string而且是拷贝返回不是引用所以函数返回值直接赋给外部字符串没问题。有一个隐蔽坑FastWriter默认会在输出末尾加一个换行符\n这是为了让输出适合写入文件。如果你要把 JSON 拼进网络协议帧里需要手动pop_back()或者用字符串裁剪去掉末尾换行。同理StyledWriter输出末尾也有换行但它的末尾换行是缩进格式的一部分保留比较合理。4.3 中文内容的输出编码统一std::string 与 UTF-8 的约定这是 jsoncpp 在 VC6.0 场景下最容易出问题的章节。jsoncpp 内部的所有字符串处理都按 UTF-8 字节流理解它不会做编码转换。你的程序里如果直接写中文字符串字面量比如value[name] 温度传感器那这个字符串在 VC6 的源码里是按本地代码页中文系统下是 GBK存储的字节序列jsoncpp 会把它原样写入 JSON 字符串值里不做任何转译。把这个 JSON 输出到任何一个按 UTF-8 解析的设备或网页时中文就乱码了。// 错误示范直接赋值中文字面量 value[name] 温度传感器; // 字符串内是 GBK 字节 // 正确示范先转成 UTF-8 字节序列再赋值 value[name] gbkToUtf8(温度传感器);这段代码的gbkToUtf8函数在资源包里有独立的实现核心原理是先用MultiByteToWideChar把 GBK 字节转成 UTF-16 的宽字符再用WideCharToMultiByte把宽字符转成 UTF-8 字节。VC6 的源码文件默认是 GBK 编码所以字符串字面量天然是 GBK 字节流。资源里给的做法是所有中文输入统一经过这个转换函数所有从 JSON 里读出来的中文字符串如果要显示到 Windows 窗口也要反向转成 GBK。5. 解析中文内容实例GBK 与 UTF-8 的字节层面换算5.1 中文乱码的根本原因源码编码、执行编码与 JSON 编码的三方不一致解决中文乱码的关键不在于 jsoncpp而在于把三个环节的编码理顺。第一个环节是 VC6 源码文件里的字符串字面量编码中文 Windows 系统上默认是 GBK第二个环节是程序运行时字符串在内存里的编码字面量是什么字节存进去就是什么字节第三个环节是 jsoncpp 解析和生成的 JSON 数据要求是 UTF-8。这三个环节只要有两个不一致就会出现乱码。我遇到过的典型场景是设备端上报的 JSON 数据是 UTF-8 编码的设备名称:温度计解析到Value对象里是正常的 UTF-8 字节然后用 StyledWriter 打印到屏幕上调试控制台按 GBK 显示乱码。这不是 jsoncpp 的问题是调试输出环节的编码没转换。反过来程序里硬编码的中文写入 JSON 再发给设备设备按 UTF-8 解码同样乱码。所以整个数据处理链路里任何涉及外部接口的字符串都必须明确标注是 UTF-8 还是 GBK。5.2 源头修复用 UTF-8 编码保存源文件直接从编译期统一一个能根治部分问题的土办法在 VC6 里把包含中文字面量的源文件保存成 UTF-8 with BOM 编码。VC6 的编译器对 UTF-8 with BOM 的源代码文件会把字符串字面量按 UTF-8 字节存入目标文件也就是说运行时内存里的字节已经和 JSON 要求的 UTF-8 一致了。这样代码里直接写value[name] 温度传感器也不会乱码。但这个做法有三个副作用需要注意。第一VC6 的编辑器对 UTF-8 with BOM 的支持不完整打开文件时可能乱码显示但不影响编译结果。第二如果代码里同时有 GBK 编码的源文件和 UTF-8 编码的源文件字符串拼接时会出现半个中文的奇怪字节排查很困难。第三资源包里自带的示例代码有些是 GBK 编码保存的你如果混用需要在工程设置里区分不能一刀切。// 用 UTF-8 with BOM 保存源文件后字面量本身已是 UTF-8 // 可以直接赋值不需要运行时转换 value[device_name] 温度传感器; // 但控制台输出仍需转换UTF-8 - GBK std::string name value[device_name].asString(); std::string displayName utf8ToGbk(name.c_str()); printf(%s\n, displayName.c_str());注意这段代码里的utf8ToGbk是资源包提供的反向转换函数和 4.3 节的gbkToUtf8正好是互逆操作。采用 UTF-8 源文件方案时控制台输出前需要做utf8ToGbk转换否则调试窗口还是乱码。这个方案的好处是省掉了写入 JSON 前的转换步骤缺点是所有的调试输出都要带转换没有哪种方案是绝对省事的关键是统一。5.3 运行时动态转换的完整案例解析设备上报的 UTF-8 中文 JSON大多数智能设备的 JSON 都是 UTF-8 编码所以解析端需要把 UTF-8 转成 GBK 才能正常显示或写入本地数据库。下面这个函数读取设备返回的文件解析后把中文内容转成 GBK 再打印完整的处理链路如下。std::wstring utf8ToUnicode(const char* utf8Str) { int len MultiByteToWideChar(CP_UTF8, 0, utf8Str, -1, NULL, 0); wchar_t* buffer new wchar_t[len]; MultiByteToWideChar(CP_UTF8, 0, utf8Str, -1, buffer, len); std::wstring result(buffer); delete[] buffer; return result; } std::string unicodeToGbk(const wchar_t* wideStr) { int len WideCharToMultiByte(CP_ACP, 0, wideStr, -1, NULL, 0, NULL, NULL); char* buffer new char[len]; WideCharToMultiByte(CP_ACP, 0, wideStr, -1, buffer, len, NULL, NULL); std::string result(buffer); delete[] buffer; return result; } std::string utf8ToGbk(const char* utf8Str) { std::wstring unicodeStr utf8ToUnicode(utf8Str); return unicodeToGbk(unicodeStr.c_str()); }这三个函数形成了完整的转换链路UTF-8 字节先转成 UTF-16 宽字符再由宽字符转成系统 ANSI 编码即 GBK。MultiByteToWideChar和WideCharToMultiByte是 Windows APIVC6 完全支持不需要额外链接库kernel32 默认已导入。每次转换都要先调用一次函数拿缓冲区长度再分配内存做第二次调用这是 Windows 编码转换的标准套路。注意返回值里的-1表示输入字符串以 null 结尾函数会处理结尾字符你不需要手动截断。6. 避坑实测记录VC6 jsoncpp 的编译期与运行期暗礁6.1 链接错误 LNK2005CRT 库版本冲突现象工程编译通过链接时报LNK2005 _malloc already defined in LIBCMT.lib有时还会伴随LNK4098警告。原因jsoncpp 编译成静态库时用的运行库选项和使用端工程不一致。VC6 的 C 运行时库有单线程/ML、多线程/MT、多线程调试/MTd、动态链接/MD等模式静态库如果用了 /ML符号定义在 LIBCMT.lib 和 LIBCD.lib 里各有一份链接时就会冲突。解决在 jsoncpp 静态库工程和使用端工程里把Project - Settings - C/C - Code Generation - Use run-time library改成完全相同的选项。我一般统一用Debug Multithreaded做调试版Release Multithreaded做发布版。改完重新编译两个工程问题消失。6.2 解析返回 false 且错误信息指向第一行BOM 头作怪现象用reader.parse解析从文件读入的 JSON 时返回falsegetFormatedErrorMessages报line 1, column 1附近有非法字符但用文本编辑器查看 JSON 文件完全正常。原因JSON 文件被 Windows 记事本或其他编辑器保存成了带 UTF-8 BOM 的格式文件头部多出 3 个字节EF BB BFjsoncpp 解析器不认 BOM把它当成非法字符。解决解析前对文件流做 BOM 检测和剥离。读文件后检查前三个字节是否是0xEF 0xBB 0xBF是则跳过。或者用std::ifstream打开文件后先fin std::ws不能跳过 BOM BOM 不是空白符需要手动处理。资源包里的读文件函数已经内置了 BOM 剥离逻辑但如果你自己写解析函数记得加上。6.3 解析大文件时程序崩溃递归深度与内存分配现象解析一个几十层嵌套的 JSON 文件或者数组长度超过几千时程序直接崩溃没有异常信息。原因jsoncpp 在旧版本里解析嵌套层级过深的对象时递归调用会导致栈溢出数组过大时append操作频繁重新分配内存VC6 的默认堆管理器效率低而且容易产生内存碎片。jsoncpp 对解析深度没有硬限制但运行时栈只有 1MB。解决一是限制 JSON 的嵌套层级设备协议里明确约定不超过 10 层二是解析前用Json::Reader::getFeatures()里的strictRoot选项把根节点必须是对象或数组这一约束打开防止根节点是数字或字符串这种非法 JSON 被解析三是如果 JSON 体量很大改用Json::CharReader新版接口配合std::istream流式解析不要一次把整个文件读入内存。6.4 中文转出来的 JSON 在设备端显示成 UTF-8 乱码现象本机解析和生成 JSON 一切正常但把 JSON 发到设备或上传到 Web 服务端后中文变成一堆乱码而且不是\uXXXX形式。原因数据在电脑和服务端之间传输时代码用的是 GBK 字节串但没声明编码服务端默认按 UTF-8 解码或者反过来。jsoncpp 在生成 JSON 时不会做转义中文字符如果以原始 UTF-8 字节写入字符串值传输层再用 GBK 读就乱码。解决在 HTTP 或 TCP 协议头里明确charsetutf-8并确保发送前字符串是 UTF-8 字节。排查时用一个十六进制查看器看字符串的第一个字节0xE4开头是 UTF-8中文通常以 E4-E9 开头0xC4或0xD6开头是 GBK。资源包里的案例工程自带这个排查工具函数打印每个中文字符的十六进制值。6.5 用 StyledWriter 输出后字符串被截断或内容偏移现象StyledWriter写出的 JSON 字符串复制到别的编辑器里偶尔出现内容偏移字符串值的末尾少了一个字符或者转义符号被吞掉。原因VC6 的std::string和 C 字符串 API 混用。StyledWriter::write返回的std::string在写入文件时用了fwrite(str.c_str(), 1, str.length(), fp)但 c_str() 返回的指针可能在后续字符串对象析构时失效如果你先保存了指针再用就等于野指针。解决凡是 Writer 的输出一律立即用std::string的值拷贝到自己的变量里不要长期持有const char*。写入文件的长度用str.length()而不是strlen(str.c_str())因为 JSON 字符串内可能包含\0转义序列的存储形式strlen会提前截断。这些细节在 VC6 的老标准下特别容易踩现代 C 里 std::string 的优化策略反而让这些问题不容易暴露。7. 最后的技巧把中文转换封装成工程级的全局工具收尾要落地一个能直接抄走的东西。资源包里已经附带了一个独立的工具模块叫encoding_util.cpp把 5.3 节里的 UTF-8 与 GBK 互转函数封装成了CEncodingUtil静态类并且加了一个自动识别函数传入一个std::string先看字节特征判断是否为 UTF-8是则转 GBK否则原样返回。这个函数在调试串口设备数据时特别实用因为有些设备的固件版本不同输出的 JSON 编码不统一用自动识别可以少写很多分支判断。std::string CEncodingUtil::AutoToGbk(const std::string input) { // 判断是否为 UTF-8 编码 bool isUtf8 true; const unsigned char* data (const unsigned char*)input.c_str(); int len (int)input.length(); int i 0; while (i len) { if (data[i] 0x80) { i; // ASCII 范围直接跳过 } else if ((data[i] 0xE0) 0xC0 i 1 len) { if ((data[i1] 0xC0) ! 0x80) { isUtf8 false; break; } i 2; } else if ((data[i] 0xF0) 0xE0 i 2 len) { if ((data[i1] 0xC0) ! 0x80 || (data[i2] 0xC0) ! 0x80) { isUtf8 false; break; } i 3; } else { isUtf8 false; break; } } if (isUtf8 len 0) { return utf8ToGbk(input.c_str()); } return input; }这个自动识别函数不算百分之百准确但对于常见的中文场景足够。UTF-8 的多字节序列有严格的比特位模式GBK 的编码空间和它有部分重叠极少数情况下会有误判但概率极低。把这个函数放在公共模块里所有项目共用一份比每个 cpp 文件里各写各的转换函数要靠谱得多。验证转换是否正确的方法也很简单调用Json::StyledWriter把生成的 JSON 打印出来在 VC6 的 Output 窗口里如果中文显示正常说明内存中的字节已经是 GBK如果在 Output 窗口乱码但文件内容正常说明是 UTF-8。也可以用资源里带的一个小工具函数PrintHex把字符串每个字节的十六进制打出来对照 ASCII 表查看E4 B8 AD是“中”的 UTF-8 编码D6 D0是“中”的 GBK 编码一眼就能分辨当前串是什么编码。资源包里所有的示例工程默认已经做了三件事源文件统一保存为 UTF-8 with BOM、工程编译选项预置了_MSC_VER1200和JSON_NO_INT64、所有读取 JSON 文件的入口都做了 BOM 剥离。你拿到手以后第一件事不是改代码而是先跑通自带的 Demo 工程确认你的 VC6 环境和资源包配置一致再往自己的工程里迁移。从那以后我每次在 VC6 里接入 jsoncpp都强制自己先跑一遍编码自检函数再动业务代码这么多年再没因为乱码翻过车。希望帮到你。本文还有配套的精品资源点击获取
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表