【字符集问题】一文看懂SBCS、MBCS、Unicode、TCHAR和相关函数

文章目录

    • 单字节和多字节字符集:SBCS vs MBCS
      • 单字节字符集(SBCS)
      • 多字节字符集(MBCS)
      • SBCS和MBCS的数据类型
    • Unicode:宽字符集
    • TCHAR.H中的通用文本映射
      • 通用文本映射的预处理程序指令
      • 通用文本数据类型的映射
      • TCHAR映射举例
    • 字符处理函数
    • 参考链接

单字节和多字节字符集:SBCS vs MBCS

单字节字符集(SBCS)

  • ASCII字符集定义的是0x00 - 0x7F范围内的字符。
    • 还有许多其他字符集,主要是欧洲字符集,它们在与0x字符集相同的范围内定义0x00 - 0x7F范围内的字符,并且还定义了0x80 - 0xFF的扩展字符集。
  • 因此,一个8位的单字节字符集(SBCS)足以表示ASCII字符集以及许多欧洲语言的字符集。但是,一些非欧洲字符集(如日语汉字)包含的字符数多于单字节编码方案中可以表示的字符数,因此需要多字节字符集(MBCS)编码。

多字节字符集(MBCS)

  • 多字节字符集可以包含单字节和双字节字符。因此,多字节字符串可以包含单字节和双字节字符的混合。
  • 双字节多字节字符具有前导字节和跟踪字节。在特定的多字节字符集中,前导字节落在特定范围内,跟踪字节也是如此。当这些范围重叠时,可能需要评估特定上下文以确定给定字节是作为前导字节还是跟踪字节。

SBCS和MBCS的数据类型

处理字符串上下文中的多字节字节或字符的MBCS要求将多字节字符串表示为unsigned char指针。
多字节字符的每个字节可以用8位字符表示。但是,类型为char且值大于0x7F 的SBCS或MBCS单字节字符为负数。当这样的字符直接转换为int或long时,结果会被编译器进行符号扩展,因此会产生意外的结果。

  • 最好将多字节字符的字节表示为8位unsigned char,以避免负的结果。
    由于某些SBCS字符串处理函数采用(带符号)char *参数,因此在定义_MBCS时将产生类型不匹配编译器警告。
    有三种方法可以避免此警告,按效率顺序列出:
  1. 在TCHAR.H中使用类型安全的内联函数。这是默认行为。
  2. 通过在命令行上定义_MB_MAP_DIRECT,在TCHAR.H中使用直接宏。如果执行此操作,则必须手动匹配类型。这是最快的方法,但不是类型安全的。
  3. 在TCHAR.H中使用类型安全的静态链接库函数。为此,请在命令行上定义常量_NO_INLINING。这是最慢的方法,但是最安全的类型。

Unicode:宽字符集

  • Unicode 是 16 位字符编码、 为适用于所有语言提供足够的编码。 所有的 ASCII 字符以 unicode 格式作为加宽字符包含在内。
  • 宽字符是2字节多语言字符代码。全世界现代计算中使用的任何字符,包括技术符号和特殊发布字符,都可以根据Unicode规范表示为宽字符。由包括Microsoft在内的大型联盟开发和维护,Unicode标准现已被广泛接受。
  • 支持多字节字符集 (MBCS) 的窗体在所有平台上称为双字节字符集 (DBCS)。
    DBCS 字符由 1 或 2 个字节构成。 某些范围的字节将留出使用用作前导字节。 前导字节指定它和以下结尾字节构成单个的 2 个字节宽字符。 在特定的多字节字符集中,前导字节位于某个范围内,尾字节也是如此。 这些范围重叠时,可能需要计算上下文以确定某个给定的字节用作前导字节还是尾字节。

宽字符的类型为wchar_t。宽字符串表示为wchar_t []数组,并由wchar_t*指针指向。
可以通过在字符前添加L前缀来将任何ASCII字符表示为宽字符。例如,L’\ 0’是终止符的宽字符(16位)。类似地,也可以将任何ASCII字符串表示为宽字符串,只需在ASCII字符串前加上字母L(L“Hello”)。

通常,宽字符在内存中占用的空间比多字节字符多,但处理速度更快。此外,在多字节编码中一次只能表示一个语言环境,而世界上的所有字符集都由Unicode表示同时表示。

TCHAR.H中的通用文本映射

为简化各种国际市场的代码开发,Microsoft运行时库为许多数据类型和对象提供Microsoft特定的“通用文本”映射。这些映射在TCHAR.H中定义
可以使用这些名称映射来编写ASCII(SBCS),MBCS或Unicode三种字符集中任何一种的通用代码,具体取决于使用#define语句定义的常量。通用文本映射是不兼容ANSI的Microsoft扩展。

通用文本映射的预处理程序指令

#define 编译版
_UNICODE Unicode(宽字符) _tcsrev 映射到 _wcsrev
_MBCS 多字节字符 _tcsrev 映射到 _mbsrev
无(默认值:既未定义_UNICODE,也未定义_MBCS) SBCS(ASCII) _tcsrev 映射到 strrev

例如,通用的文本功能_tcsrev,在TCHAR.H定义,如果MBCS已被定义则映射到mbsrev,如果_UNICODE已被定义则映射到_wcsrev,否则_tcsrev映射到strrev。

通用文本数据类型的映射

通用文本数据类型名称 SBCS(_UNICODE,_MBCS未定义) _MBCS已定义 _UNICODE已定义
_TCHAR char char wchar_t
_TINT int int wint_t
_TSCHAR signed char signed char wchar_t
_TUCHAR unsigned char unsigned char wchar_t
_TXCHAR char unsigned char wchar_t
_T 或 _TEXT 没有效果 没有效果 L (将后续字符或字符串转换为Unicode项)

TCHAR映射举例

以下代码片段说明了使用_TCHAR和_tcsrev映射到MBCS,Unicode和SBCS模型。

_TCHAR *RetVal, *szString;
RetVal = _tcsrev(szString);

如果MBCS已定义,则预处理器将前面的代码映射到以下代码:

char *RetVal, *szString;
RetVal = _mbsrev(szString);

如果_UNICODE已定义,则预处理器将相同的代码映射到以下代码:

wchar_t *RetVal, *szString;
RetVal = _wcsrev(szString);

如果既未定义_MBCS,也未定义_UNICODE,则预处理器将代码映射到单字节ASCII代码,如下所示:

char *RetVal, *szString;
RetVal = strrev(szString);

因此,可以编写、维护和编译统一的源代码文件,实现三种字符集下的运行。

字符处理函数

常规 使用 setlocale类别设置依赖
atof,_atof_l,_wtof,_wtof_l 将字符转换为浮点值 LC_NUMERIC
atoi,_atoi_l,_wtoi,_wtoi_l 将字符转换为整数值 LC_NUMERIC
_atoi64,_atoi64_l,_wtoi64,_wtoi64_l 将字符转换为64位整数值 LC_NUMERIC
atol,_atol_l,_wtol,_wtol_l 将字符转换为长值 LC_NUMERIC
_atodbl,_atodbl_l,_atoldbl,_atoldbl_l,_atoflt,_atoflt_l 将字符转换为double-long值 LC_NUMERIC
是常规 针对特定条件测试给定整数。 LC_CTYPE
isleadbyte,_isleadbyte_l 测试引导字节 LC_CTYPE
localeconv 读取用于格式化数字量的适当值 LC_MONETARY, LC_NUMERIC
MB_CUR_MAX 当前语言环境中任何多字节字符的最大长度(以字节为单位)(在STDLIB.H中定义的宏) LC_CTYPE
_mbccpy,_mbccpy_l,_mbccpy_s,_mbccpy_s_l 复制一个多字节字符 LC_CTYPE
_mbclen,mblen,_mblen_l 验证并返回多字节字符的字节数 LC_CTYPE
strlen,wcslen,_mbslen,_mbslen_l,_mbstrlen,_mbstrlen_l 对于多字节字符串:验证字符串中的每个字符; 返回字符串长度 LC_CTYPE
mbstowcs,_mbstowcs_l,mbstowcs_s,_mbstowcs_s_l 将多字节字符序列转换为相应的宽字符序列 LC_CTYPE
mbtowc,_mbtowc_l 将多字节字符转换为相应的宽字符 LC_CTYPE
printf函数 写格式化输出 LC_NUMERIC(确定基数字符输出)
scanf功能 读取格式化输入 LC_NUMERIC(确定基数字符识别)
setlocale,_wsetlocale 选择程序的区域设置 不适用
strcoll,wcscoll,_mbscoll,_strcoll_l,_wcscoll_l,_mbscoll_l 比较两个字符串的字符 LC_COLLATE
_stricmp,_wcsicmp,_mbsicmp,_stricmp_l,_wcsicmp_l,_mbsicmp_l 比较两个字符串而不考虑大小写 LC_CTYPE
_stricoll,_wcsicoll,_mbsicoll,_stricoll_l,_wcsicoll_l,_mbsicoll_l 比较两个字符串的字符(不区分大小写) LC_COLLATE
_strncoll,_wcsncoll,_mbsncoll,_strncoll_l,_wcsncoll_l,_mbsncoll_l 比较两个字符串的前n个字符 LC_COLLATE
_strnicmp,_wcsnicmp,_mbsnicmp,_strnicmp_l,_wcsnicmp_l,_mbsnicmp_l 比较两个字符串的字符而不考虑大小写。 LC_CTYPE
_strnicoll,_wcsnicoll,_mbsnicoll,_strnicoll_l,_wcsnicoll_l,_mbsnicoll_l 比较两个字符串的前n个字符(不区分大小写) LC_COLLATE
strftime,wcsftime,_strftime_l,_wcsftime_l 根据提供的格式参数格式化日期和时间值 LC_TIME
_strlwr,_wcslwr,_mbslwr,_strlwr_l,_wcslwr_l,_mbslwr_l,_strlwr_s,_strlwr_s_l,_mbslwr_s,_mbslwr_s_l,_wcslwr_s,_wcslwr_s_l 将给定字符串中的每个大写字母转换为小写 LC_CTYPE
strtod,_strtod_l,wcstod,_wcstod_l 将字符串转换为double值 LC_NUMERIC(确定基数字符识别)
strtol,wcstol,_strtol_l,_wcstol_l 将字符串转换为长值 LC_NUMERIC(确定基数字符识别)
strtoul,_strtoul_l,wcstoul,_wcstoul_l 将字符串转换为无符号长值 LC_NUMERIC(确定基数字符识别)
_strupr,_strupr_l,_mbsupr,_mbsupr_l,_wcsupr_l,_wcsupr,_strupr_s,_strupr_s_l,_mbsupr_s,_mbsupr_s_l,_wcsupr_s,_wcsupr_s_l 将字符串中的每个小写字母转换为大写 LC_CTYPE
strxfrm,wcsxfrm,_strxfrm_l,_wcsxfrm_l 根据区域设置将字符串转换为整理形式 LC_COLLATE
tolower,_tolower,towlower,_tolower_l,_towlower_l,_mbctolower,_mbctolower_l,_mbctoupper,_mbctoupper_l 将给定字符转换为相应的小写字符 LC_CTYPE
toupper,_toupper,towupper,_toupper_l,_towupper_l,_mbctolower,_mbctolower_l,_mbctoupper,_mbctoupper_l 将给定字符转换为相应的大写字母 LC_CTYPE
wcstombs,_wcstombs_l,wcstombs_s,_wcstombs_s_l 将宽字符序列转换为多字节字符的对应序列 LC_CTYPE
wctomb,_wctomb_l,wctomb_s,_wctomb_s_l 将宽字符转换为相应的多字节字符 LC_CTYPE

注意:

对于多字节代码,多字节代码页必须等同于使用setlocale设置的语言环境。_setmbcp,参数为_MB_CP_LOCALE,使多字节代码页与setlocale代码页相同。

参考链接

https://docs.microsoft.com/en-us/cpp/c-runtime-library/internationalization?view=vs-2019

你可能感兴趣的:(C/C++)