C++ Builder 參考手冊(cè) ? C++ Builder 的字符串類型、字符類型、字符編碼
- 字符變量
- 字符常數(shù)
- 字符串常數(shù)
- 標(biāo)準(zhǔn) C / C++ 字符串變量類型
- Windows API 字符串變量類型
- C++ Builder 字符串變量類型
- UTF-8 / UTF-16 / UTF-32 / ANSI / GBK / BIG5 等編碼轉(zhuǎn)換
一. 字符變量
| 變量類型 | 說明 |
|---|---|
| char | 一個(gè)字節(jié)的字符變量類型,有符號(hào)或無符號(hào) 8 位整數(shù)【注1】, UTF-8 或 ANSI / ASCII 編碼 【注2】 |
| wchar_t | 寬字符變量類型,2 或 4 個(gè)字節(jié),UTF-16 或 UTF-32 編碼, 操作系統(tǒng) API 函數(shù)的寬字符類型【注3】 |
| char16_t | 2 個(gè)字節(jié)的字符變量類型,UTF-16 編碼 |
| char32_t | 4 個(gè)字節(jié)的字符變量類型,UTF-32 編碼 |
| _TCHAR | C 語言頭文件 <tchar.h> 里面的變量類型, 項(xiàng)目設(shè)置里面的 _TCHAR maps to 選項(xiàng) 可以設(shè)此類型為 wchar_t 或 char |
| TCHAR | Windows API 里面的字符變量類型,與 _TCHAR 類型相同 |
【注1】char 類型在不同的平臺(tái)里面,可能是有符號(hào)整數(shù) (x86 / x64),也可能是無符號(hào)整數(shù) (ARM / PowerPC)。大多數(shù)編譯器里面的 char 都是 8 位的整數(shù),雖然 C / C++ 標(biāo)準(zhǔn)里面沒有規(guī)定 char 的位數(shù),但是說明了 char 必須支持 UTF-8 編碼 (C++ 14),那么 char 就應(yīng)該是 8 位整數(shù)。
【注2】char 類型的字符編碼,可能是 UTF-8 類型的 (Linux 默認(rèn)編碼 / Windows 10 1903 之后的版本在控制面板里面設(shè)定 UTF-8 編碼,如下面的截圖所示),也可能是 ANSI 編碼 (Windows 到目前為止的所有的版本的默認(rèn)的編碼)。
【注3】wchar_t 類型在不同的平臺(tái)里面,可能是 2 個(gè)字節(jié)的 UTF-16 編碼的字符類型 (Windows),也可能是 4 個(gè)字節(jié)的 UTF-32 編碼的字符類型 (Linux)。
Windows 10 1903 之后版本的控制面板里面的 UTF-8 編碼選項(xiàng),打勾之后,"字符串"、char、std::string 和 AnsiString 都變成了 UTF-8 編碼:

二. 字符常數(shù)
| 字符常數(shù) | 說明 |
|---|---|
| 'c' | 單引號(hào)里面只能有一個(gè)字符【注4】,是一個(gè)字符的常數(shù), 這個(gè)常數(shù)的值是一個(gè)整數(shù),int 或 unsigned int 類型【注5】, 等于這個(gè)字符的編碼值,UTF-8 或 ANSI【注6】 |
| L'c' | 前綴為大寫英文字母 L 的單引號(hào)里面只能有一個(gè)字符, 是一個(gè)字符的常數(shù),wchar_t 類型的, 數(shù)值等于這個(gè)字符的編碼值,UTF-16 或 UTF-32【注7】 |
| u'c' | 前綴為小寫英文字母 u 的單引號(hào)里面只能有一個(gè)字符, 是一個(gè)字符的常數(shù),char16_t 類型的, 數(shù)值等于這個(gè)字符的編碼值,UTF-16編碼【注8】 |
| U'c' | 前綴為大寫英文字母 U 的單引號(hào)里面只能有一個(gè)字符, 是一個(gè)字符的常數(shù),char32_t 類型的, 數(shù)值等于這個(gè)字符的編碼值,UTF-32 編碼 |
| _T('c') | C 語言頭文件 <tchar.h> 里面的變量類型, 項(xiàng)目設(shè)置里面的 _TCHAR maps to 選項(xiàng) 設(shè)為 wchar_t 或 char 相當(dāng)于 L'c' 或 'c' |
| _TEXT('c') | 與 _T('c') 相同 |
| TEXT('c') | Windows API 里面的字符常數(shù),與 _T('c') 相同 |
【注4】'c' 單引號(hào)里面只能有一個(gè)字符,不限于編碼為 1 個(gè)字節(jié)的字符 (英文字母與數(shù)字等),也可以有超過一個(gè)字節(jié)的編碼的字符,比如漢字等,例如 '漢' 和 '字' 都可以,單引號(hào)的字符并不是 char 類型的,而是 int 或 unsigned 類型的,如果給 char 賦值,高位字節(jié)丟失,只剩下最低位的一個(gè)字節(jié)的值,這種情況,編譯器可能會(huì)給出警告。
【注5】'c' 或 '漢' 這樣的字符常數(shù),是 int 或 unsigned int 類型的,對(duì)于 C++ Builder,如果使用 clang 編譯器,是 int 類型的,如果使用 Borland 編譯器,是 unsigned int 類型的,其他 C/C++ 開發(fā)工具沒有測(cè)試。clang 編譯器超過 1 個(gè)字節(jié)的編碼的字符常數(shù)會(huì)有警告,因?yàn)橥ǔ_@樣的字符要給 char 賦值,會(huì)丟失高位字節(jié)。
【注6】'c' 或 '漢' 這樣的字符常數(shù)的字符編碼,可能是 UTF-8 類型的 (Linux 默認(rèn)編碼 / Windows 10 1903 之后的版本在控制面板里面設(shè)定 UTF-8 編碼,如本文前面 char 類型的備注的截圖所示的參數(shù)位置),也可能是 ANSI 編碼 (Windows 到目前為止的所有的版本的默認(rèn)的編碼)。
【注7】L'c' 或 L'漢' 這樣的字符常數(shù)的字符編碼和 wchar_t 類型相同,可能是 UTF-32 編碼 (Linux),也可能是 UTF-16 編碼 (Windows)。如果是 UTF-16 編碼,存在 2 個(gè) char16_t 字符的編碼 (4 個(gè)字節(jié)的編碼),如果使用的是 Borland 編譯器,丟失第二個(gè) char16_t,只剩下第一個(gè) char16_t。例如 U+1F642 的 Emoji 字符 L'??' 的 UTF-16 編碼為 0xD83D, 0xDE42 兩個(gè) char16_t 字符,Borland 編譯器這個(gè)字符的編碼值只剩下了 0xD83D。如果使用 clang 編譯器,2 個(gè) char16_t 的編碼的字符無法編譯通過,即 L'漢' 可以得到正確的編碼值,L'??' 就無法編譯通過了,這樣的字符需要用字符串處理。
【注8】u'c' 或 u'漢' 這樣的字符常數(shù)為 UTF-16 編碼的,如果這個(gè)字符是 2 個(gè) char16_t 編碼的,例如 U+1F642 的 Emoji 字符 L'??' 的 UTF-16 編碼為 0xD83D, 0xDE42 兩個(gè) char16_t 字符,就無法編譯通過了,這樣的字符需要用字符串處理。
通過以上注釋,字符常數(shù)的總結(jié):
- UTF-8 或 ANSI 超過 1 個(gè)字節(jié)的編碼要用字符串處理,單個(gè)字符的字符常數(shù)的值超過了 1 個(gè)字節(jié)對(duì)于不同的編譯器的表現(xiàn)不同,可能無法正確處理;
- UTF-16 編碼的字符如果是由 2 個(gè) char16_t 組成的,不同的編譯器的表現(xiàn)不同,并且都無法正確處理,所以這樣的字符需要用字符串處理;
- UTF-32 編碼的字符永遠(yuǎn)都是正確的,他們的編碼值就等于 UNICODE 編碼值。
三. 字符串常數(shù)
| 字符串常數(shù) | 說明 |
|---|---|
| "字符串" | UTF-8 或 ANSI 編碼的字符串【注9】 |
| L"字符串" | 前綴為大寫英文字母 L 的字符串, UTF-16 或 UTF-32 編碼【注10】 |
| u"字符串" | 前綴為小寫英文字母 u 的字符串,UTF-16編碼 |
| U"字符串" | 前綴為大寫英文字母 U 的字符串,UTF-32 編碼 |
| _T("字符串") | C 語言頭文件 <tchar.h> 里面的變量類型, 項(xiàng)目設(shè)置里面的 _TCHAR maps to 選項(xiàng) 設(shè)為 wchar_t 或 char 相當(dāng)于 L"字符串" 或 "字符串" |
| _TEXT("字符串") | 與 _T("字符串") 相同 |
| TEXT("字符串") | Windows API 里面的字符常數(shù),與 _T("字符串") 相同 |
【注9】"字符串" 這樣的字符串常數(shù)的字符編碼,可能是 UTF-8 類型的 (Linux 默認(rèn)編碼 / Windows 10 1903 之后的版本在控制面板里面設(shè)定 UTF-8 編碼,如本文前面 char 類型的備注的截圖所示的參數(shù)位置),也可能是 ANSI 編碼 (Windows 到目前為止的所有的版本的默認(rèn)的編碼)。
【注10】L"字符串" 這樣的字符串常數(shù)的字符編碼,可能是 2 個(gè)字節(jié)的 UTF-16 編碼的字符類型 (Windows),也可能是 4 個(gè)字節(jié)的 UTF-32 編碼的字符類型 (Linux)。
四. 標(biāo)準(zhǔn) C / C++ 字符串變量類型
| 變量類型 | 說明 |
|---|---|
| char * | 字符指針,可以用做字符串變量,UTF-8 或 ANSI 編碼【注11】 |
| wchar_t * | 寬字符指針,UTF-16 或 UTF-32 編碼【注12】 |
| char16_t * | UTF-16 字符指針 |
| char32_t * | UTF-32 字符指針 |
| _TCHAR * | _TCHAR 字符指針,請(qǐng)參考 _TCHAR 字符變量類型, 在項(xiàng)目設(shè)置里面的 _TCHAR maps to 選項(xiàng) 可以設(shè) _TCHAR 類型為 wchar_t 或 char |
| TCHAR * | Windows API 里面的類型,同 _TCHAR * |
| char[] | 字符數(shù)組,可以用做字符串變量,UTF-8 或 ANSI 編碼【注11】 |
| wchar_t[] | 寬字符數(shù)組,UTF-16 或 UTF-32 編碼【注12】 |
| char16_t[] | UTF-16 字符數(shù)組 |
| char32_t[] | UTF-32 字符數(shù)組 |
| _TCHAR[] | _TCHAR 字符數(shù)組,請(qǐng)參考 _TCHAR 字符變量類型, 在項(xiàng)目設(shè)置里面的 _TCHAR maps to 選項(xiàng) 可以設(shè) _TCHAR 類型為 wchar_t 或 char |
| TCHAR[] | Windows API 里面的類型,同 _TCHAR[] |
| std::string | STL 里面的字符串,UTF-8 或 ANSI 編碼【注11】 |
| std::wstring | STL 里面的字符串,UTF16 或 UTF32 編碼【注12】 |
| std::u16string | STL 里面的字符串,UTF-16 編碼 |
| std::u32string | STL 里面的字符串,UTF-32 編碼 |
【注11】char * / char [] / std::string 這些的字符串的字符編碼,可能是 UTF-8 類型的 (Linux 默認(rèn)編碼 / Windows 10 1903 之后的版本在控制面板里面設(shè)定 UTF-8 編碼,如本文前面 char 類型的備注的截圖所示的參數(shù)位置),也可能是 ANSI 編碼 (Windows 到目前為止的所有的版本的默認(rèn)的編碼)。
【注12】wchar_t * / wchar_t [] / std::wstring 這些字符串的字符編碼,可能是 UTF-16 編碼的字符串 (Windows),也可能是 UTF-32 編碼的字符串 (Linux)。
五. Windows API 字符串變量類型
| API 類型 | C 語言類型 |
|---|---|
| CHAR | char |
| PCHAR | char * |
| PSTR | char * |
| LPSTR | char * |
| PCSTR | const char * |
| LPCSTR | const char * |
| WCHAR | wchar_t |
| PWCHAR | wchar_t * |
| PWSTR | wchar_t * |
| LPWSTR | wchar_t * |
| PCWSTR | const wchar_t * |
| LPCWSTR | const wchar_t * |
| PTSTR | _TCHAR * |
| LPTSTR | _TCHAR * |
| PCTSTR | const _TCHAR * |
| LPCTSTR | const _TCHAR * |
| BSTR | 雖然看上去是 wchar_t *,但不是 C / C++ 的字符串類型, 而是微軟的 COM 的字符串類型, 前 4 個(gè)字節(jié)是長(zhǎng)度,接下來是字符串內(nèi)容,然后是結(jié)束符, 指針指向第一個(gè)字符,而不是內(nèi)存首地址, 所以從指針指向的內(nèi)容來看像是 C 語言的字符串。 |
六. C++ Builder 字符串變量類型
| 變量類型 | 說明 |
|---|---|
| UnicodeString | UTF-16 編碼的字符串, C++ Builder 最常用的字符串類型 |
| UTF8String | UTF-8 編碼的字符串 |
| AnsiString | ANSI 編碼的字符串,代碼頁為 0 的字符串【注11】, typedef AnsiStringT<0> AnsiString; |
| AnsiStringT<CP> | 代碼頁為 CP 的字符串,例如: AnsiStringT<936> 為 GBK 編碼的字符串, AnsiStringT<950> 為 BIG5 編碼的字符串,AnsiStringT<65001> 為 UTF-8 編碼的字符串 |
| String | UNICODE 版本為 UnicodeString; ANSI 版本為 AnsiString |
| RawByteString | 相當(dāng)于 char * 類型的封裝, 不處理字符編碼,不進(jìn)行編碼轉(zhuǎn)換 |
| ShortString | 只能和 AnsiString 之間互相賦值,字符串長(zhǎng)度在 0 到 255 之間,固定占用 256 個(gè)字節(jié) |
| SmallString<sz> | 只能和 AnsiString 之間互相賦值,字符串長(zhǎng)度在 0 到 sz 之間,固定占用 sz + 1 個(gè)字節(jié) |
| UCS4String | UTF-32 / UCS4 編碼, 只用作編碼轉(zhuǎn)換,不能參與字符串運(yùn)算 |
| WideString | BSTR 類型的封裝,微軟的 COM 字符串類型 |
七. UTF-8 / UTF-16 / UTF-32 / ANSI / GBK / BIG5 等編碼轉(zhuǎn)換
1. UTF-8 / UTF-16 / ANSI / GBK / BIG5 等編碼轉(zhuǎn)換
UnicodeString、UTF8String、AnsiString、AnsiStringT<CP> 這些字符串之間互相賦值可以自動(dòng)轉(zhuǎn)碼。
void __fastcall TForm1::Button1Click(TObject *Sender)
{
UnicodeString u16s = L"你好,玄坴!";
UTF8String u8s = u16s;
AnsiStringT<936> gbk = u8s;
AnsiStringT<950> big5 = u16s;
AnsiString as = big5;
Memo1->Lines->Add(u16s);
Memo1->Lines->Add(u8s );
Memo1->Lines->Add(gbk );
Memo1->Lines->Add(big5);
Memo1->Lines->Add(as );
wchar_t *lpU16 = u16s.c_str(); // UTF-16
char *lpUTF8 = u8s.c_str(); // UTF-8
char *lpGBK = gbk.c_str(); // GBK
char *lpBIG5 = big5.c_str(); // BIG5
char *lpANSI = as.c_str(); // ANSI
Memo1->Lines->Add(L"---");
Memo1->Lines->Add(lpU16 );
Memo1->Lines->Add(lpUTF8);
Memo1->Lines->Add(lpGBK );
Memo1->Lines->Add(lpBIG5);
Memo1->Lines->Add(lpANSI);
UnicodeString sU16 = lpU16 ; // UTF-16
UTF8String sU8 = lpUTF8; // UTF-8
AnsiStringT<936> s936 = lpGBK ; // GBK
AnsiStringT<950> s950 = lpBIG5; // BIG5
AnsiString sANSI = lpANSI; // ANSI
Memo1->Lines->Add(L"---");
Memo1->Lines->Add(sU16 );
Memo1->Lines->Add(sU8 );
Memo1->Lines->Add(s936 );
Memo1->Lines->Add(s950 );
Memo1->Lines->Add(sANSI );
}
在控制面板里面選擇了 UTF-8 編碼,編譯運(yùn)行:
- 由于 UnicodeString、UTF8String、AnsiString、AnsiStringT<CP> 這些字符串會(huì)自動(dòng)轉(zhuǎn)碼,所以這樣的字符串顯示出來都不亂碼;
- char * 字符串只有和控制面板的編碼相同時(shí)不會(huì)亂碼,編碼不同會(huì)亂碼;
- 把 char * 放回對(duì)應(yīng)編碼的字符串類型里面,就不亂碼了,因?yàn)樗麄儠?huì)自動(dòng)轉(zhuǎn)碼。


在控制面板里面選擇了中文(簡(jiǎn)體,中國(guó)),編譯運(yùn)行:
- 由于 UnicodeString、UTF8String、AnsiString、AnsiStringT<CP> 這些字符串會(huì)自動(dòng)轉(zhuǎn)碼,所以這樣的字符串顯示出來都不亂碼;
- char * 字符串只有和控制面板的編碼相同時(shí)不會(huì)亂碼,編碼不同會(huì)亂碼;
- 把 char * 放回對(duì)應(yīng)編碼的字符串類型里面,就不亂碼了,因?yàn)樗麄儠?huì)自動(dòng)轉(zhuǎn)碼。


2. UTF-32 與其他編碼之間轉(zhuǎn)換
由于 Windows 核心都是 UTF-16 編碼的,沒有處理 UTF-32 編碼的能力,如果有 UTF-32 編碼的數(shù)據(jù)需要轉(zhuǎn)成 UTF-16 處理。
由于 UTF-32 編碼和 UCS4 編碼相同,可以用這兩個(gè)函數(shù)來進(jìn)行編碼轉(zhuǎn)換:
UCS4String __fastcall UnicodeStringToUCS4String(const UnicodeString S);
UnicodeString __fastcall UCS4StringToUnicodeString(const UCS4String S);
UCS4String 字符串也沒有處理字符串的能力,只是 UTF-32 字符的動(dòng)態(tài)數(shù)組,只用來編碼轉(zhuǎn)換,這個(gè)字符串類型是這樣定義的:
typedef DynamicArray<UCS4Char> UCS4String;
相關(guān):
C++ Builder 參考手冊(cè) ? C++ Builder 的字符串類型、字符類型、字符編碼