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 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 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 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 的字符串,例如:
AnsiStringT<936> 为 GBK 编码的字符串,
AnsiStringT<950> 为 BIG5 编码的字符串,AnsiStringT<65001> 为 UTF-8 编码的字符串
String UNICODE 版本为 UnicodeString;
ANSI 版本为 AnsiString
RawByteString 相当于 char * 类型的封装,
不处理字符编码,不进行编码转换
ShortString 只能和 AnsiString 之间互相赋值,字符串长度在 0 到 255 之间,固定占用 256 个字节
SmallString 只能和 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 这些字符串之间互相赋值可以自动转码。

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

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

  • 由于 UnicodeString、UTF8String、AnsiString、AnsiStringT 这些字符串会自动转码,所以这样的字符串显示出来都不乱码;
  • 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 UCS4String;

相关:

  • C++ Builder 整型变量和整型常量
  • C++ Builder 的枚举类型
  • C++ Builder 浮点型变量和浮点型常量

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

你可能感兴趣的:(C++ Builder 的字符串类型、字符类型、字符编码)