C++ Builder 的字符串类型、字符类型、字符编码

C++ Builder 参考手册C++ Builder 的字符串类型、字符类型、字符编码


  • 字符变量
  • 字符常数
  • 字符串常数
  • 标准 C / C++ 字符串变量类型
  • Windows API 字符串变量类型
  • C++ Builder 字符串变量类型
  • UTF-8 / UTF-16 / UTF-32 / ANSI / GBK / BIG5 等编码转换

一. 字符变量

变量类型 说明
char 一个字节的字符变量类型,有符号或无符号 8 位整数【注1】,
UTF-8 或 ANSI / ASCII 编码 【注2】
wchar_t 宽字符变量类型,2 或 4 个字节,UTF-16 或 UTF-32 编码,
操作系统 API 函数的宽字符类型【注3】
char16_t 2 个字节的字符变量类型,UTF-16 编码
char32_t 4 个字节的字符变量类型,UTF-32 编码
_TCHAR C 语言头文件 <tchar.h> 里面的变量类型,
项目设置里面的 _TCHAR maps to 选项
可以设此类型为 wchar_t 或 char
TCHAR Windows API 里面的字符变量类型,与 _TCHAR 类型相同

【注1】char 类型在不同的平台里面,可能是有符号整数 (x86 / x64),也可能是无符号整数 (ARM / PowerPC)。大多数编译器里面的 char 都是 8 位的整数,虽然 C / C++ 标准里面没有规定 char 的位数,但是说明了 char 必须支持 UTF-8 编码 (C++ 14),那么 char 就应该是 8 位整数。

【注2】char 类型的字符编码,可能是 UTF-8 类型的 (Linux 默认编码 / Windows 10 1903 之后的版本在控制面板里面设定 UTF-8 编码,如下面的截图所示),也可能是 ANSI 编码 (Windows 到目前为止的所有的版本的默认的编码)。

【注3】wchar_t 类型在不同的平台里面,可能是 2 个字节的 UTF-16 编码的字符类型 (Windows),也可能是 4 个字节的 UTF-32 编码的字符类型 (Linux)。

Windows 10 1903 之后版本的控制面板里面的 UTF-8 编码选项,打勾之后,"字符串"、char、std::string 和 AnsiString 都变成了 UTF-8 编码:

Windows 10 的字符编码改为 UTF-8 的选项

二. 字符常数

字符常数 说明
'c' 单引号里面只能有一个字符【注4】,是一个字符的常数,
这个常数的值是一个整数,int 或 unsigned int 类型【注5】,
等于这个字符的编码值,UTF-8 或 ANSI【注6】
L'c' 前缀为大写英文字母 L 的单引号里面只能有一个字符,
是一个字符的常数,wchar_t 类型的,
数值等于这个字符的编码值,UTF-16 或 UTF-32【注7】
u'c' 前缀为小写英文字母 u 的单引号里面只能有一个字符,
是一个字符的常数,char16_t 类型的,
数值等于这个字符的编码值,UTF-16编码【注8】
U'c' 前缀为大写英文字母 U 的单引号里面只能有一个字符,
是一个字符的常数,char32_t 类型的,
数值等于这个字符的编码值,UTF-32 编码
_T('c') C 语言头文件 <tchar.h> 里面的变量类型,
项目设置里面的 _TCHAR maps to 选项
设为 wchar_t 或 char 相当于 L'c' 或 'c'
_TEXT('c') 与 _T('c') 相同
TEXT('c') Windows API 里面的字符常数,与 _T('c') 相同

【注4】'c' 单引号里面只能有一个字符,不限于编码为 1 个字节的字符 (英文字母与数字等),也可以有超过一个字节的编码的字符,比如汉字等,例如 '汉' 和 '字' 都可以,单引号的字符并不是 char 类型的,而是 int 或 unsigned 类型的,如果给 char 赋值,高位字节丢失,只剩下最低位的一个字节的值,这种情况,编译器可能会给出警告。

【注5】'c' 或 '汉' 这样的字符常数,是 int 或 unsigned int 类型的,对于 C++ Builder,如果使用 clang 编译器,是 int 类型的,如果使用 Borland 编译器,是 unsigned int 类型的,其他 C/C++ 开发工具没有测试。clang 编译器超过 1 个字节的编码的字符常数会有警告,因为通常这样的字符要给 char 赋值,会丢失高位字节。

【注6】'c' 或 '汉' 这样的字符常数的字符编码,可能是 UTF-8 类型的 (Linux 默认编码 / Windows 10 1903 之后的版本在控制面板里面设定 UTF-8 编码,如本文前面 char 类型的备注的截图所示的参数位置),也可能是 ANSI 编码 (Windows 到目前为止的所有的版本的默认的编码)。

【注7】L'c' 或 L'汉' 这样的字符常数的字符编码和 wchar_t 类型相同,可能是 UTF-32 编码 (Linux),也可能是 UTF-16 编码 (Windows)。如果是 UTF-16 编码,存在 2 个 char16_t 字符的编码 (4 个字节的编码),如果使用的是 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'汉' 这样的字符常数为 UTF-16 编码的,如果这个字符是 2 个 char16_t 编码的,例如 U+1F642 的 Emoji 字符 L'🙂' 的 UTF-16 编码为 0xD83D, 0xDE42 两个 char16_t 字符,就无法编译通过了,这样的字符需要用字符串处理。

通过以上注释,字符常数的总结:

  • UTF-8 或 ANSI 超过 1 个字节的编码要用字符串处理,单个字符的字符常数的值超过了 1 个字节对于不同的编译器的表现不同,可能无法正确处理;
  • UTF-16 编码的字符如果是由 2 个 char16_t 组成的,不同的编译器的表现不同,并且都无法正确处理,所以这样的字符需要用字符串处理;
  • UTF-32 编码的字符永远都是正确的,他们的编码值就等于 UNICODE 编码值。

三. 字符串常数

字符串常数 说明
"字符串" UTF-8 或 ANSI 编码的字符串【注9】
L"字符串" 前缀为大写英文字母 L 的字符串,
UTF-16 或 UTF-32 编码【注10】
u"字符串" 前缀为小写英文字母 u 的字符串,UTF-16编码
U"字符串" 前缀为大写英文字母 U 的字符串,UTF-32 编码
_T("字符串") C 语言头文件 <tchar.h> 里面的变量类型,
项目设置里面的 _TCHAR maps to 选项
设为 wchar_t 或 char 相当于 L"字符串" 或 "字符串"
_TEXT("字符串") 与 _T("字符串") 相同
TEXT("字符串") Windows API 里面的字符常数,与 _T("字符串") 相同

【注9】"字符串" 这样的字符串常数的字符编码,可能是 UTF-8 类型的 (Linux 默认编码 / Windows 10 1903 之后的版本在控制面板里面设定 UTF-8 编码,如本文前面 char 类型的备注的截图所示的参数位置),也可能是 ANSI 编码 (Windows 到目前为止的所有的版本的默认的编码)。

【注10】L"字符串" 这样的字符串常数的字符编码,可能是 2 个字节的 UTF-16 编码的字符类型 (Windows),也可能是 4 个字节的 UTF-32 编码的字符类型 (Linux)。

四. 标准 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 字符变量类型,
在项目设置里面的 _TCHAR maps to 选项
可以设 _TCHAR 类型为 wchar_t 或 char
TCHAR * Windows API 里面的类型,同 _TCHAR *
char[] 字符数组,可以用做字符串变量,UTF-8 或 ANSI 编码【注11】
wchar_t[] 宽字符数组,UTF-16 或 UTF-32 编码【注12】
char16_t[] UTF-16 字符数组
char32_t[] UTF-32 字符数组
_TCHAR[] _TCHAR 字符数组,请参考 _TCHAR 字符变量类型,
在项目设置里面的 _TCHAR maps to 选项
可以设 _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 默认编码 / Windows 10 1903 之后的版本在控制面板里面设定 UTF-8 编码,如本文前面 char 类型的备注的截图所示的参数位置),也可能是 ANSI 编码 (Windows 到目前为止的所有的版本的默认的编码)。

【注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 个字节是长度,接下来是字符串内容,然后是结束符,
指针指向第一个字符,而不是内存首地址,
所以从指针指向的内容来看像是 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 相当于 char * 类型的封装,
不处理字符编码,不进行编码转换
ShortString 只能和 AnsiString 之间互相赋值,字符串长度在 0 到 255 之间,固定占用 256 个字节
SmallString<sz> 只能和 AnsiString 之间互相赋值,字符串长度在 0 到 sz 之间,固定占用 sz + 1 个字节
UCS4String UTF-32 / UCS4 编码,
只用作编码转换,不能参与字符串运算
WideString BSTR 类型的封装,微软的 COM 字符串类型

七. UTF-8 / UTF-16 / UTF-32 / ANSI / GBK / BIG5 等编码转换

1. UTF-8 / UTF-16 / ANSI / GBK / BIG5 等编码转换

UnicodeString、UTF8String、AnsiString、AnsiStringT<CP> 这些字符串之间互相赋值可以自动转码。

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> 这些字符串会自动转码,所以这样的字符串显示出来都不乱码;
  • char * 字符串只有和控制面板的编码相同时不会乱码,编码不同会乱码;
  • 把 char * 放回对应编码的字符串类型里面,就不乱码了,因为他们会自动转码。
控制面板里面选择了 UTF-8 编码
编码转换运行结果

在控制面板里面选择了中文(简体,中国),编译运行:

  • 由于 UnicodeString、UTF8String、AnsiString、AnsiStringT<CP> 这些字符串会自动转码,所以这样的字符串显示出来都不乱码;
  • char * 字符串只有和控制面板的编码相同时不会乱码,编码不同会乱码;
  • 把 char * 放回对应编码的字符串类型里面,就不乱码了,因为他们会自动转码。
控制面板里面选择了中文(简体,中国)
编码转换运行结果

2. UTF-32 与其他编码之间转换

由于 Windows 核心都是 UTF-16 编码的,没有处理 UTF-32 编码的能力,如果有 UTF-32 编码的数据需要转成 UTF-16 处理。

由于 UTF-32 编码和 UCS4 编码相同,可以用这两个函数来进行编码转换:

UCS4String __fastcall UnicodeStringToUCS4String(const UnicodeString S);
UnicodeString __fastcall UCS4StringToUnicodeString(const UCS4String S);

UCS4String 字符串也没有处理字符串的能力,只是 UTF-32 字符的动态数组,只用来编码转换,这个字符串类型是这样定义的:

typedef DynamicArray<UCS4Char> UCS4String;

相关:


C++ Builder 参考手册C++ Builder 的字符串类型、字符类型、字符编码

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,924评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,902评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,716评论 0 239
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,783评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,166评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,510评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,784评论 2 311
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,476评论 0 196
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,196评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,459评论 2 243
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,978评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,321评论 2 252
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,964评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,046评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,803评论 0 193
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,530评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,420评论 2 265

推荐阅读更多精彩内容