C++ Builder 參考手冊 ? 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 | 一個字節(jié)的字符變量類型埂软,有符號或無符號 8 位整數(shù)【注1】锈遥, UTF-8 或 ANSI / ASCII 編碼 【注2】 |
wchar_t | 寬字符變量類型,2 或 4 個字節(jié)勘畔,UTF-16 或 UTF-32 編碼所灸, 操作系統(tǒng) API 函數(shù)的寬字符類型【注3】 |
char16_t | 2 個字節(jié)的字符變量類型,UTF-16 編碼 |
char32_t | 4 個字節(jié)的字符變量類型咖杂,UTF-32 編碼 |
_TCHAR | C 語言頭文件 <tchar.h> 里面的變量類型庆寺, 項目設(shè)置里面的 _TCHAR maps to 選項 可以設(shè)此類型為 wchar_t 或 char |
TCHAR | Windows API 里面的字符變量類型,與 _TCHAR 類型相同 |
【注1】char 類型在不同的平臺里面诉字,可能是有符號整數(shù) (x86 / x64)懦尝,也可能是無符號整數(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 類型在不同的平臺里面,可能是 2 個字節(jié)的 UTF-16 編碼的字符類型 (Windows)权谁,也可能是 4 個字節(jié)的 UTF-32 編碼的字符類型 (Linux)剩檀。
Windows 10 1903 之后版本的控制面板里面的 UTF-8 編碼選項,打勾之后旺芽,"字符串"沪猴、char康二、std::string 和 AnsiString 都變成了 UTF-8 編碼:
二. 字符常數(shù)
字符常數(shù) | 說明 |
---|---|
'c' | 單引號里面只能有一個字符【注4】焕参,是一個字符的常數(shù)葛菇, 這個常數(shù)的值是一個整數(shù)痹扇,int 或 unsigned int 類型【注5】, 等于這個字符的編碼值担租,UTF-8 或 ANSI【注6】 |
L'c' | 前綴為大寫英文字母 L 的單引號里面只能有一個字符砸民, 是一個字符的常數(shù),wchar_t 類型的奋救, 數(shù)值等于這個字符的編碼值阱洪,UTF-16 或 UTF-32【注7】 |
u'c' | 前綴為小寫英文字母 u 的單引號里面只能有一個字符, 是一個字符的常數(shù)菠镇,char16_t 類型的, 數(shù)值等于這個字符的編碼值承璃,UTF-16編碼【注8】 |
U'c' | 前綴為大寫英文字母 U 的單引號里面只能有一個字符利耍, 是一個字符的常數(shù),char32_t 類型的盔粹, 數(shù)值等于這個字符的編碼值隘梨,UTF-32 編碼 |
_T('c') | C 語言頭文件 <tchar.h> 里面的變量類型, 項目設(shè)置里面的 _TCHAR maps to 選項 設(shè)為 wchar_t 或 char 相當(dāng)于 L'c' 或 'c' |
_TEXT('c') | 與 _T('c') 相同 |
TEXT('c') | Windows API 里面的字符常數(shù)舷嗡,與 _T('c') 相同 |
【注4】'c' 單引號里面只能有一個字符轴猎,不限于編碼為 1 個字節(jié)的字符 (英文字母與數(shù)字等),也可以有超過一個字節(jié)的編碼的字符进萄,比如漢字等捻脖,例如 '漢' 和 '字' 都可以,單引號的字符并不是 char 類型的中鼠,而是 int 或 unsigned 類型的可婶,如果給 char 賦值,高位字節(jié)丟失援雇,只剩下最低位的一個字節(jié)的值矛渴,這種情況,編譯器可能會給出警告惫搏。
【注5】'c' 或 '漢' 這樣的字符常數(shù)具温,是 int 或 unsigned int 類型的,對于 C++ Builder筐赔,如果使用 clang 編譯器铣猩,是 int 類型的,如果使用 Borland 編譯器川陆,是 unsigned int 類型的剂习,其他 C/C++ 開發(fā)工具沒有測試蛮位。clang 編譯器超過 1 個字節(jié)的編碼的字符常數(shù)會有警告,因為通常這樣的字符要給 char 賦值鳞绕,會丟失高位字節(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 個 char16_t 字符的編碼 (4 個字節(jié)的編碼)冒签,如果使用的是 Borland 編譯器,丟失第二個 char16_t钟病,只剩下第一個 char16_t萧恕。例如 U+1F642 的 Emoji 字符 L'??' 的 UTF-16 編碼為 0xD83D, 0xDE42 兩個 char16_t 字符,Borland 編譯器這個字符的編碼值只剩下了 0xD83D肠阱。如果使用 clang 編譯器票唆,2 個 char16_t 的編碼的字符無法編譯通過,即 L'漢' 可以得到正確的編碼值屹徘,L'??' 就無法編譯通過了走趋,這樣的字符需要用字符串處理。
【注8】u'c' 或 u'漢' 這樣的字符常數(shù)為 UTF-16 編碼的噪伊,如果這個字符是 2 個 char16_t 編碼的簿煌,例如 U+1F642 的 Emoji 字符 L'??' 的 UTF-16 編碼為 0xD83D, 0xDE42 兩個 char16_t 字符,就無法編譯通過了鉴吹,這樣的字符需要用字符串處理啦吧。
通過以上注釋,字符常數(shù)的總結(jié):
- UTF-8 或 ANSI 超過 1 個字節(jié)的編碼要用字符串處理拙寡,單個字符的字符常數(shù)的值超過了 1 個字節(jié)對于不同的編譯器的表現(xiàn)不同授滓,可能無法正確處理;
- UTF-16 編碼的字符如果是由 2 個 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> 里面的變量類型, 項目設(shè)置里面的 _TCHAR maps to 選項 設(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 個字節(jié)的 UTF-16 編碼的字符類型 (Windows)乡翅,也可能是 4 個字節(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 字符指針,請參考 _TCHAR 字符變量類型靶累, 在項目設(shè)置里面的 _TCHAR maps to 選項 可以設(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ù)組争舞,請參考 _TCHAR 字符變量類型凛忿, 在項目設(shè)置里面的 _TCHAR maps to 選項 可以設(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 個字節(jié)是長度,接下來是字符串內(nèi)容咒劲,然后是結(jié)束符顷蟆, 指針指向第一個字符诫隅,而不是內(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 * 類型的封裝, 不處理字符編碼绕辖,不進行編碼轉(zhuǎn)換 |
ShortString | 只能和 AnsiString 之間互相賦值摇肌,字符串長度在 0 到 255 之間,固定占用 256 個字節(jié) |
SmallString<sz> | 只能和 AnsiString 之間互相賦值仪际,字符串長度在 0 到 sz 之間围小,固定占用 sz + 1 個字節(jié) |
UCS4String | UTF-32 / UCS4 編碼, 只用作編碼轉(zhuǎ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> 這些字符串之間互相賦值可以自動轉(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 編碼,編譯運行:
- 由于 UnicodeString挣输、UTF8String纬凤、AnsiString、AnsiStringT<CP> 這些字符串會自動轉(zhuǎn)碼撩嚼,所以這樣的字符串顯示出來都不亂碼停士;
- char * 字符串只有和控制面板的編碼相同時不會亂碼,編碼不同會亂碼完丽;
- 把 char * 放回對應(yīng)編碼的字符串類型里面向瓷,就不亂碼了,因為他們會自動轉(zhuǎn)碼舰涌。
在控制面板里面選擇了中文(簡體猖任,中國),編譯運行:
- 由于 UnicodeString瓷耙、UTF8String朱躺、AnsiString刁赖、AnsiStringT<CP> 這些字符串會自動轉(zhuǎn)碼,所以這樣的字符串顯示出來都不亂碼长搀;
- char * 字符串只有和控制面板的編碼相同時不會亂碼宇弛,編碼不同會亂碼;
- 把 char * 放回對應(yīng)編碼的字符串類型里面源请,就不亂碼了枪芒,因為他們會自動轉(zhuǎn)碼。
2. UTF-32 與其他編碼之間轉(zhuǎn)換
由于 Windows 核心都是 UTF-16 編碼的舅踪,沒有處理 UTF-32 編碼的能力,如果有 UTF-32 編碼的數(shù)據(jù)需要轉(zhuǎn)成 UTF-16 處理良蛮。
由于 UTF-32 編碼和 UCS4 編碼相同抽碌,可以用這兩個函數(shù)來進行編碼轉(zhuǎn)換:
UCS4String __fastcall UnicodeStringToUCS4String(const UnicodeString S);
UnicodeString __fastcall UCS4StringToUnicodeString(const UCS4String S);
UCS4String 字符串也沒有處理字符串的能力,只是 UTF-32 字符的動態(tài)數(shù)組决瞳,只用來編碼轉(zhuǎn)換货徙,這個字符串類型是這樣定義的:
typedef DynamicArray<UCS4Char> UCS4String;
相關(guān):
C++ Builder 參考手冊 ? C++ Builder 的字符串類型、字符類型皮胡、字符編碼