陳鍾誠

Version 1.0

#C 語言中的寬字串 – 包含 Unicode

要在 C 語言中使用 Unicode 字串,假如您用的是 gcc 編譯器或 Linux,您可以使用寬字元 wchar_t 這個形態,以取代 char,然後用對應的函數取代原本的字串函數,以下是常見字串函數的寬字元版對應表。

窄字元寬字元說明
strlen()wcslen()字串長度
strcat()wcscat字串連接
strcmp()wcscmp()字串比較
strcoll()wcscoll()字串比較 (不分大小寫)
strcpy()wcscpy()字串複製
strchr()wcschr()尋找字元
strstr()wcswcs()尋找字串
strtok()wcstok()字串分割
strcspn()wcscspn()傳回字串中第一個符合字元集的位置
strpbrk()wcspbrk()傳回字串中第一個符合字元集的指標
strxfrm()wcsxfrm()根據區域設定 locale() 轉換字元集

根據區域設定 locale() 轉換字元集 簡而言之,就是將原本 strXXX() 函數,轉換成 wcsXXX() 函數,然後照著原本的方法使用,只是對象從 char* 改為 wchar_t * 即可,請看下列範例。

####程式範例:Unicode 寬字串處理函數

檔案:unicode.c

#include <stdio.h>
#include <locale.h>

int main(void)
{
    if (!setlocale(LC_CTYPE, "")) {
        fprintf(stderr, "Error:Please check LANG, LC_CTYPE, LC_ALL.\n");
        return 1;
    }
    wchar_t *str1=L"Hi!你好"; // 輸出結果 (範例)
    printf("str1=%ls\n", str1); // str1=Hi!你好
    printf("wcslen(str1)=%d\n", wcslen(str1)); // wcslen(str1)=5
    printf("wcschr(str1,%lc)=%d\n", L'好', wcschr(str1, L'好')); // wcschr(str1,好)=4206648
    printf("wcswcs(str1,%ls)=%d\n", L"你好", wcsstr(str1, L"你好")); // wcswcs(str1,你好)=4206646
    printf("wcsspn(str1,aeiou)=%d\n", wcsspn(str1, L"aeiou")); // wcsspn(str1,aeiou)=0
    printf("wcsspn(str1,EFGH)=%d\n", wcsspn(str1, L"EFGH")); // wcsspn(str1,EFGH)=1
    printf("address(str1)=%p\n", str1); // address(str1)=00403030
    printf("wcssbrk(str1,aeiou)=%p\n", wcspbrk(str1, L"aeiou")); // wcssbrk(str1,aeiou)=00403032
    wchar_t str2[20];
    wcscpy(str2, str1);
    printf("str2=%ls\n", str2); // str2=Hi!你好
    printf("wcscmp(str1,str2)=%d\n", wcscmp(str1, str2)); // wcscmp(str1,str2)=0
    wcscat(str2, L",我是John");
    printf("str2=%ls\n", str2); // str2=Hi!你好,我是John
    return 0;
}

####執行結果

D:\cp>gcc unicode.c -o unicode

D:\cp>unicode
str1=Hi!你好
wcslen(str1)=5
wcschr(str1,好)=4206648
wcswcs(str1,你好)=4206646
wcsspn(str1,aeiou)=0
wcsspn(str1,EFGH)=1
address(str1)=00403030
wcssbrk(str1,aeiou)=00403032
str2=Hi!你好
wcscmp(str1,str2)=0
str2=Hi!你好,我是John

####後記

寬字串的處理函數有很多,並不限於上列的函數,幾乎所有具有字串的標準 C 函數都有寬版,關於更多的寬版函數請參考下列網頁。

http://www.java2s.com/Tutorial/C/0300__Wide-Character-String/WideCharacterFunctions.htm

####來自 jserv 的建議

"""
寬字串函數 — 寬字串的處理,在 C 語言中,通常寬字串指的是 Unicode (但不限定於 Unicode)
"""

wide-character 翻譯為「寬字串」,我覺得有本質的問題。

以下摘錄 CLDP:
http://linux.org.tw/CLDP/OLD/doc/i18n-introduction.html

"wcs" 是 "wide-chararater string" 的縮寫,而 "mbs" 是 "multi-byte string"
的縮寫,二者分別代表字串的表現方式。所謂的 multi-byte 是指數個 char 組成 一個字 (如全形字或中文字是由兩個 char
組成),而 wide-char 是指一個 wchar_t type 就是一個字, 而 sizeof(wchar_t)
的大小與系統有關,一般而言是 4 bytes。 一般我們可以直接看、輸出輸入等都是 multi-byte, 如:
    char *str = "這是一個句子: abcd";

但我們會建議在程式內部,用 mbstowcs() 將它轉成 wchar_t 來統一處理,這個 轉換其實是根據 locale 中的
LC_CTYPE 的機制,它定義了 multi-byte 與 wide- char
值二者間的對應關係。做這樣轉換的好處是,您不用擔心全形、半形的問題, 因為一個 wchar_t 矩陣元就是一個字。

wchar_t 有一組與 string.h 中相對應的字串處理函式,就定義在 wchar.h 中,讓我們可以如同處理 (char *) 那樣
地處理 (wchar_t *), 其部分的對應關係如下,其他的可以直接看 wchar.h 的內容:

    wcscpy()        <====>          strcpy()
    wcsncpy()       <====>          strncpy()
    wcslen()        <====>          strlen()
    wcsdup()        <====>          strdup()
    wcscmp()        <====>          strcmp()
    wcsncmp()       <====>          strncmp()
    ........................................

由於 mbs 碼與 wcs 碼的對應關係是由該 locale 的 LC_CTYPE 來決定的,也就是不 同的 locale
寫法其對應關係可能會不一樣。就我們的 glibc2, zh_TW.Big5 locale 而言,由 mbs 轉成的 wcs 即為
unicode (有關 unicode 的資訊可以在 http://www.unicode.org/
中找到),但不能保證在其他的系統或環境下也是如此。 故最保險的做法,是將字串儲存成 multi-byte, 然後在 run-time 時才用
mbstowcs() 轉成 wide-char 來運作。

==> 可以看出重點不在於字串 (C 語言的 string 只是一個寫法,本質上仍是連續記憶體) 的「寬度」,而是 character 與
character set 的「範圍寬度」
==> 建議保留原文 "wide character",真要翻譯的話,可寫「擴充字元」

####參考文獻