1. 構建
iconv是一個用于在不同字符編碼(如 UTF-8、GBK、ISO-8859-1 等)之間進行轉換的開源庫。筆者在《c++中utf8字符串和gbk字符串的轉換》這篇文章中介紹過如何在Windows下實現utf8字符串和gbk字符串的轉換,不過該實現是基于Win32 API的,在其他平臺中是無法使用的。如果需要跨平臺,那么就需要使用iconv這樣的庫來統一實現。
不過麻煩的是iconv是GNU/Linux項目提供的庫,不提供CMake的構建方式,以及原生的MSCV的構建方式。在Windows下的構建官方推薦使用MSYS2來進行構建。不過MSYS2構建出來的成果不一定能與MSVC構建的成果二進制兼容,而在Windows下還是使用MSVC的情況比較多。所以這就有點僵住了,只能尋求第三方的幫助。
這里筆者的解決方案是直接使用vcpkg。vcpkg是微軟開發的C/C++跨平臺開源庫管理工具,試用了一下,感覺確實比以前進步很多,如果不是像筆者一樣有自己的需求,完全可以都使用vcpkg來安裝依賴庫。
通過以下指令下載并安裝iconv:
git clone https://github.com/microsoft/vcpkg
cd vcpkg
.\bootstrap-vcpkg.bat
.\vcpkg install libiconv:x64-windows
iconv就會安裝在vcpkg的目錄下,如下圖所示:
iconv是個底層庫,不需要其他依賴庫,因此可以直接復制到筆者的倉庫中使用,算是滿足了筆者的需求。另外,不知道vcpkg的機制是什么,vcpkg確實也使用了cmake來構建,因為生成了cmake的配置文件,可以直接被CMake項目集成使用。最后,默認情況下vcpkg會檢測環境內的VS,使用最高版本的VS來編譯鏈接,構建的時候要保證與目標版本一致。
2. 示例
最后就直接給一個CMake項目調用剛才安裝好的iconv庫的示例吧。因為vcpkg在安裝iconv的時候,也安裝了相應的cmake的配置文件,所以可以直接在CMakeLists.txt集成,關鍵配置代碼是:
# 項目代碼設置為utf-8編碼
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")message(">> using Clang")
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")message(">> using GCC")
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel")message(">> using Intel C++")
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")message(">> using Visual Studio C++")add_compile_options("/utf-8")
else()message(">> unknow compiler.")
endif()find_package(Iconv REQUIRED)# ...target_link_libraries(${PROJECT_NAME} PRIVATE Iconv::Iconv)
注意,find_package
要生效,需要保證CMake能夠搜索到相應的庫。如果是直接使用的vcpkg,那么需要將vcpkg集成到CMake搜索路徑中:
vcpkg integrate install
如果是像筆者一樣,是將iconv復制到自己倉庫中使用,那么需要在CMake的內置變量CMAKE_PREFIX_PATH中增加自己的倉庫路徑(比如修改CMakePresets.json文件中CMAKE_PREFIX_PATH的配置)。
將utf8編碼字符串轉換成gbk字符串的代碼示例如下:
#include <iconv.h>using namespace std;int main() {// 原始 UTF-8 字符串const char *utf8_str = "你好,世界!";printf("%s\n", utf8_str);size_t in_bytes_left = strlen(utf8_str);char in_buf[1024];strcpy(in_buf, utf8_str);char *in_ptr = in_buf;// 輸出緩沖區(GBK)char out_buf[1024];char *out_ptr = out_buf;size_t out_bytes_left = sizeof(out_buf);// 打開 iconv 轉換器:從 UTF-8 轉換到 GBKiconv_t cd = iconv_open("GBK", "UTF-8");if (cd == (iconv_t)-1) {perror("iconv_open failed");return 1;}// 執行轉換if (iconv(cd, &in_ptr, &in_bytes_left, &out_ptr, &out_bytes_left) ==(size_t)(-1)) {perror("iconv failed");iconv_close(cd);return 1;}// 關閉轉換器iconv_close(cd);// 獲取實際轉換后的長度size_t converted_len = sizeof(out_buf) - out_bytes_left;// 直接寫入二進制字節到 stdout(不經過 printf,防止轉碼)fwrite(out_buf, 1, converted_len, stdout);return 0;
}
運行結果如下所示:
浣犲ソ錛屼笘鐣岋紒
你好,世界!