用戶體驗第一的現代C++ JSON庫 支持中文


轉自:

用戶體驗第一的現代C++ JSON庫

// 最簡單的方式創建一個json對象 json obj = { { "null", nullptr }, { "number", 1 }, { "float", 1.3 }, { "boolean", false }, { "string", "中文測試" }, { "array", { 1, 2, true, 1.4 } }, { "object", { "key", "value" } } }; 

有沒有覺得單看這段代碼都有種js內味了(誤)。但是沒錯,上面這段代碼是C++ !

如果這引起了你的些許興趣,那就說明這個輪子成功了。

故事在前

造輪子的初衷是在兩年前,我記得那天空中都是圓圓的輪狀雲,突然想給自己的游戲做一個json格式的配置文件。當我百度一下C++ json庫時,被一張圖震驚了

28個C/C++開源JSON庫性能對比

當然震驚我的不是這些庫的性能,而是竟然足足有28個比較火的庫在互相對比。。還有哪個語言可以做到讓這么多人樂此不疲的為了一個小功能寫那些重復的邏輯和代碼呢?

然后我選了幾個比較熱門的庫看看usage,看的越多,震驚越多。

每一個庫的接口設計都無法獲得我在審美上的認同,甚至有些demo代碼又長又臭,這時候真的是想感嘆一句:我愛Golang!(誤)

於是那天的雲彩顯得越發的圓潤。直到那天我才知道,原來C++的設計美學從底子里就是圓形的。

所以各位手下留情,這個庫的存在並不是提升了多少性能,或者支持了多少json標准,而僅僅是為了美才誕生的。

這個庫的slogan也很簡單:

願天堂沒有C++


好了,認真介紹一下這個庫:

JSONXX

一個為 C++ 量身打造的輕量級 JSON 通用工具,輕松完成 JSON 解析和序列化功能,並和 C++ 輸入輸出流交互。

Nomango/jsonxx​github.com圖標

使用介紹

  • 引入 jsonxx 頭文件
#include "jsonxx/json.hpp" using namespace jsonxx; 
  • 使用 C++ 的方式的創建 JSON 對象

使用 operator[] 為 JSON 對象賦值

json j; j["number"] = 1; j["float"] = 1.5; j["string"] = "this is a string"; j["boolean"] = true; j["user"]["id"] = 10; j["user"]["name"] = "Nomango"; 

使用 std::initializer_list 為 JSON 對象賦值

// 使用初始化列表構造數組 json arr = { 1, 2, 3 }; // 使用初始化列表構造對象 json obj = { { "user", { { "id", 10 }, { "name", "Nomango" } } } }; // 第二個對象 json obj2 = { { "nul", nullptr }, { "number", 1 }, { "float", 1.3 }, { "boolean", false }, { "string", "中文測試" }, { "array", { 1, 2, true, 1.4 } }, { "object", { "key", "value" } } }; 

使用輔助方法構造數組或對象

json arr = json::array({ 1 }); json obj = json::object({ "user", { { "id", 1 }, { "name", "Nomango" } } }); 
  • 判斷 JSON 對象的值類型
// 判斷 JSON 值類型 bool is_null(); bool is_boolean(); bool is_integer(); bool is_float(); bool is_array(); bool is_object(); 
  • 將 JSON 對象進行顯式或隱式轉換
// 顯示轉換 auto b = j["boolean"].as_boolean(); // bool auto i = j["number"].as_integer(); // int32_t auto f = j["float"].as_float(); // float const auto& arr = j["array"].as_array(); // arr 實際是 std::vector<json> 類型 const auto& obj = j["user"].as_object(); // obj 實際是 std::map<std::string, json> 類型 // 隱式轉換 bool b = j["boolean"]; int i = j["number"]; // int32_t 自動轉換為 int double d = j["float"]; // float 自動轉換成 double std::vector<json> arr = j["array"]; std::map<std::string, json> obj = j["user"]; 
若 JSON 值類型與待轉換類型不相同也不協變,會引發 json_type_error 異常
  • 取值的同時判斷類型
int n; bool ret = j["boolean"].get_value(&n); // 若取值成功,ret 為 true 
  • JSON 對象類型和數組類型的遍歷
// 增強 for 循環 for (auto& j : obj) { std::cout << j << std::endl; } // 使用迭代器遍歷 for (auto iter = obj.begin(); iter != obj.end(); iter++) { std::cout << iter.key() << ":" << iter.value() << std::endl; } 
  • JSON 解析
// 解析字符串 json j = json::parse("{ \"happy\": true, \"pi\": 3.141 }"); // 從文件讀取 JSON std::ifstream ifs("sample.json"); json j; ifs >> j; // 從標准輸入流讀取 JSON json j; std::cin >> j; 
  • JSON 序列化
// 序列化為字符串 std::string json_str = j.dump(); // 美化輸出,使用 4 個空格對輸出進行格式化 std::string pretty_str = j.dump(4, ' '); // 將 JSON 內容輸出到文件 std::ofstream ofs("output.json"); ofs << j << std::endl; // 將 JSON 內容輸出到文件,並美化 std::ofstream ofs("pretty.json"); ofs << std::setw(4) << j << std::endl; // 將 JSON 內容輸出到標准輸出流 json j; std::cout << j; // 可以使用 std::setw(4) 對輸出內容美化 

更多

若你需要將 JSON 解析和序列化應用到非 std::basic_stream 流中,可以通過創建自定義 output_adapter 和 input_adapter 的方式實現。

實際上 json::parse() 和 json::dump() 函數也是通過自定義的 string_output_adapter 和 string_input_adapter 實現對字符串內容的輸入和輸出。

詳細內容請參考 json_parser.hpp 和 json_serializer.hpp。

寫在最后

很多朋友提到這個庫的風格和 nlohmann/json 很像,確實是的,當初看到這個庫的時候我感嘆一句“果然你能想到的別人都實現過了”,然后順手“借鑒”了一些功能補充進來(誤)

所以我也很推薦這個庫,nlohmann也許是JSON for modern C++ 的最佳實踐了吧!

 


對於中文,直接在vs2019下 gbk編碼文件,賦值中文后

j["happy"] = "中文";

然后取出 j["happy"].as_string().c_str(), 發現字符是按照gbk編碼存放:D6D0 CEC4.

而windows中,存放中文:

LPCWSTR cn = L"中文";  //看到它是放的unicode編碼。

調用windows的 ::MessageBox(NULL, (LPCWSTR)( j["happy"].as_string().c_str()), cn, MB_OK); 會顯示亂碼。

利用下面的轉換一下就好了,就會將gbk轉成unicode的 2d 4e 87 65

CString str = CString(c_char);
USES_CONVERSION;
LPCWSTR wszClassName = A2CW(W2A(str));

 用這個網站觀察編碼,很不錯:http://www.mytju.com/classcode/tools/encode_gb2312.asp


 

如果本身字符是Unicode,存儲時轉成多字節,變成了gbk。讀取時再由c的char單字節str轉回多字節:

json obj;

obj[key]= wstring2string(title);

作為了string存儲。因為jsonxx不支持wstring。

讀取時 string2wstring(obj[key]) 返回了wstring;.c_str() 返回了wchar_t*.

#include <Windows.h>
//將string轉換成wstring  
wstring string2wstring(string str)  
{  
    wstring result;  
    //獲取緩沖區大小,並申請空間,緩沖區大小按字符計算  
    int len = MultiByteToWideChar(CP_ACP, 0, str.c_str(), str.size(), NULL, 0);  
    TCHAR* buffer = new TCHAR[len + 1];  
    //多字節編碼轉換成寬字節編碼  
    MultiByteToWideChar(CP_ACP, 0, str.c_str(), str.size(), buffer, len);  
    buffer[len] = '\0';             //添加字符串結尾  
    //刪除緩沖區並返回值  
    result.append(buffer);  
    delete[] buffer;  
    return result;  
}  
  
//將wstring轉換成string  
string wstring2string(wstring wstr)  
{  
    string result;  
    //獲取緩沖區大小,並申請空間,緩沖區大小事按字節計算的  
    int len = WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), wstr.size(), NULL, 0, NULL, NULL);  
    char* buffer = new char[len + 1];  
    //寬字節編碼轉換成多字節編碼  
    WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), wstr.size(), buffer, len, NULL, NULL);  
    buffer[len] = '\0';  
    //刪除緩沖區並返回值  
    result.append(buffer);  
    delete[] buffer;  
    return result;  
}

 

 

 


 

前言

大家在學習或者使用Windows編程中,經常會碰到字符串之間的轉換,char*轉LPCWSTR也是其中一個比較常見的轉換。下面就列出幾種比較常用的轉換方法。大家可以根據自己的需求選擇相對應的方法,下面來一起學習學習吧。

1、通過MultiByteToWideChar函數轉換

MultiByteToWideChar函數是將多字節轉換為寬字節的一個API函數,它的原型如下:

1
2
3
4
5
6
7
8
int MultiByteToWideChar(
  UINT CodePage,   // code page
  DWORD dwFlags,   // character-type options
  LPCSTR lpMultiByteStr, // string to map
  int cbMultiByte,  // number of bytes in string
  LPWSTR lpWideCharStr, // wide-character buffer
  int cchWideChar  // size of buffer
);

LPCWSTR實際上也是CONST WCHAR *類型

1
2
3
4
5
   char * szStr = "測試字符串" ;
WCHAR wszClassName[256];
memset (wszClassName,0, sizeof (wszClassName));
MultiByteToWideChar(CP_ACP,0,szStr, strlen (szStr)+1,wszClassName,
  sizeof (wszClassName)/ sizeof (wszClassName[0]));

2、通過T2W轉換宏

1
2
3
4
5
6
   char * szStr = "測試字符串"
CString str = CString(szStr);
USES_CONVERSION;
LPCWSTR wszClassName = new WCHAR [str.GetLength()+1];
wcscpy(( LPTSTR )wszClassName,T2W(( LPTSTR )str.GetBuffer(NULL)));
str.ReleaseBuffer();

3、通過A2CW轉換

1
2
3
4
5
char * szStr = "測試字符串"
CString str = CString(szStr);
USES_CONVERSION;
LPCWSTR wszClassName = A2CW(W2A(str));
str.ReleaseBuffer();

上述方法都是UniCode環境下測試的。

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作能帶來一定的幫助,如果有疑問大家可以留言交流。

 

 

 

 

 


網上看到的,好像最新是不是已經解決了。沒碰到這問題。

首先,待解析的json字符串如果包含中文,會出錯。

1
json j = json::parse( "{\"name\":\"中文\"}" );

錯誤  return token_type::parse_error;

解決方法,大概在json_parser.hpp 409行:

//可見utf-8中文 范圍未知,暫時都當作字符存進來 3字節

然后就可以解決了。

另外一個問題,如果返回的是unicode編碼中文,如\u4e2d,在代碼解析的時候,

1
string_buffer.push_back(char_traits::to_char_type(byte));

byte是utf編碼值,20013,明顯一個字節存不下,要把wchar轉成2個字節的char數組

1
2
3
4
char * lpStr = ( char *)_alloca(2);
WideCharToMultiByte(::GetACP(), 0, (LPCWCH)&byte, -1, lpStr, 2, NULL, NULL);
string_buffer.push_back(char_traits::to_char_type(*lpStr++));
string_buffer.push_back(char_traits::to_char_type(*lpStr));

以上代碼會改變2個字節的gbk字符串集,下面方法不會改變,保持原樣

1
2
3
4
5
6
7
8
9
10
11
12
13
if  (byte < 0x80) {
     string_buffer.push_back(char_traits::to_char_type(byte));
}
else  if  (byte < 0x800) {
     string_buffer.push_back(char_traits::to_char_type(0xc0 | (byte >> 6)));
     string_buffer.push_back(char_traits::to_char_type(0x80 | (byte & 0x3f)));
}
else
{
     string_buffer.push_back(char_traits::to_char_type(0xe0 | (byte >> 12)));
     string_buffer.push_back(char_traits::to_char_type(0x80 | ((byte >> 6) & 0x3f)));
     string_buffer.push_back(char_traits::to_char_type(0x80 | (byte & 0x3f)));
}

代碼差不多在365行左右。

就可以完美解決解析unicode中文了。

本文鏈接:https://www.it72.com/12672.htm

 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM