libiconv (GNU) 提供 iconv() 族函數,支援數百種字符集互轉 (UTF-8, UTF-16, UTF-32, GBK, Big5, ISO-8859-*, CP936, Shift_JIS 等)。是跨平台編碼轉換的標準選擇。
#include// 打開轉換描述符 iconv_t iconv_open(const char *tocode, const char *fromcode); // tocode: 目標編碼名 (如 "UTF-8") // fromcode: 來源編碼名 (如 "GBK") // 回傳: 成功返回描述符,失敗返回 (iconv_t)-1 // 執行轉換 size_t iconv(iconv_t cd, char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft); // inbuf: 輸入緩衝區位址的位址 (函數會更新指標) // inbytesleft: 輸入剩餘位元組數 (輸入/輸出參數) // outbuf: 輸出緩衝區位址的位址 // outbytesleft: 輸出剩餘空間 (輸入/輸出參數) // 回傳: 成功返回轉換不可逆字符數,失敗返回 (size_t)-1 並設 errno // 關閉描述符 int iconv_close(iconv_t cd);
| errno | 含義 |
|---|---|
| EILSEQ | 輸入位元組序列在來源編碼中無效 |
| EINVAL | 輸入不完整 (如截斷的多位元組字符) |
| E2BIG | 輸出緩衝區空間不足 |
| EBADF | cd 非有效描述符 |
// iconv_wrapper.h #ifndef ICONV_WRAPPER_H #define ICONV_WRAPPER_H #include// 簡易介面:一次性轉換,自動分配輸出緩衝區 (呼叫端需 free) char *iconv_convert(const char *fromcode, const char *tocode, const char *input, size_t inlen, size_t *outlen); // 進階介面:流式轉換器 typedef struct IconvConverter IconvConverter; IconvConverter *iconv_open2(const char *tocode, const char *fromcode); size_t iconv_convert2(IconvConverter *cv, const char *in, size_t inlen, char **out, size_t *outlen); void iconv_close2(IconvConverter *cv); #endif
// iconv_wrapper.c #include "iconv_wrapper.h" #include#include #include #include struct IconvConverter { iconv_t cd; char *outbuf; size_t outcap; }; IconvConverter *iconv_open2(const char *tocode, const char *fromcode) { IconvConverter *cv = malloc(sizeof(*cv)); if (!cv) return NULL; cv->cd = iconv_open(tocode, fromcode); if (cv->cd == (iconv_t)-1) { free(cv); return NULL; } cv->outbuf = NULL; cv->outcap = 0; return cv; } size_t iconv_convert2(IconvConverter *cv, const char *in, size_t inlen, char **out, size_t *outlen) { if (!cv || !in || !out || !outlen) return (size_t)-1; // 確保輸出緩衝區足夠 (最壞情況: UTF-8→UTF-32 擴大 4 倍) size_t need = inlen * 4 + 16; if (need > cv->outcap) { char *nb = realloc(cv->outbuf, need); if (!nb) return (size_t)-1; cv->outbuf = nb; cv->outcap = need; } char *inptr = (char*)in; // 移除 const 警告 char *outptr = cv->outbuf; size_t inleft = inlen, outleft = cv->outcap; size_t ret = iconv(cv->cd, &inptr, &inleft, &outptr, &outleft); if (ret == (size_t)-1 && errno != E2BIG) return (size_t)-1; *out = cv->outbuf; *outlen = cv->outcap - outleft; return ret; } void iconv_close2(IconvConverter *cv) { if (!cv) return; if (cv->cd != (iconv_t)-1) iconv_close(cv->cd); free(cv->outbuf); free(cv); } // 簡易一次性介面 char *iconv_convert(const char *fromcode, const char *tocode, const char *input, size_t inlen, size_t *outlen) { IconvConverter *cv = iconv_open2(tocode, fromcode); if (!cv) return NULL; char *out = NULL; size_t olen = 0; size_t ret = iconv_convert2(cv, input, inlen, &out, &olen); iconv_close2(cv); if (ret == (size_t)-1) { free(out); return NULL; } if (outlen) *outlen = olen; return out; }
#include "iconv_wrapper.h" #includeint main() { // UTF-8 → GBK const char *utf8 = "測試 Test 中文"; size_t olen; char *gbk = iconv_convert("UTF-8", "GBK", utf8, strlen(utf8), &olen); if (gbk) { printf("GBK (%zu bytes): ", olen); for (size_t i=0; i 六、編譯與鏈結
gcc -o test test.c iconv_wrapper.c -liconv # 部分系統需額外指定路徑 gcc -o test test.c iconv_wrapper.c -I/usr/local/include -L/usr/local/lib -liconv七、進階主題
- BOM 處理:UTF-16/32 轉換可能產生/需處理 BOM (
iconv通常自動處理,但可用"UTF-16LE"明確小端)。- 錯誤恢復:遇到
EILSEQ可跳過非法位元組 (手動推進inptr) 或替換為替換字符 (如?)。- 效能優化:重用
IconvConverter避免重複iconv_open/close;大檔案分塊處理避免巨大記憶體。- Windows:MSVC 可用
MultiByteToWideChar/WideCharToMultiByte原生 API,或移植 libiconv。- ICU:更強大 (正規化、排序、邊界分析),但體積龐大;簡單轉換 libiconv 輕量足夠。
八、總結
libiconv 是 C 語言跨平台編碼轉換的基石。封裝
iconv_open/iconv/iconv_close為安全、自動擴容、流式支援的介面,即可在專案中穩健處理 GBK/UTF-8/UTF-16 等互轉需求。