扩展字符支持

作者:追风剑情 发布于:2020-7-16 9:29 分类:C

C语言最初并不是作为国际编译语言设计的,其字符的选择或多或少是基于标准的美国键盘。但是,随着后来C在世界范围内越来越流行,不得不扩展来支持不同且更大的字符集。

三字符序列

有些键盘没有C中使用的所有符号,因此C提供了一些由三个字符组成的序列( 即三字符序列)作为这些符号的替换表示。

三字符序列
三字符序列 符号 三字符序列 符号 三字符序列 符号
??= # ??( [ ??/ \
??) ] ??' ^ ??< {
??! | ??> } ??- ~


??=include 
??=define LIM 100
int main()
??<
   int q??(LIM??);
   printf("More to come.??\n");
??>
会变成这样:
#include 
#define LIM 100
int main()
{
   int q[LIM];
   printf("More to come.\n");
}
当然,要在编译器中设置相关选项才能激活这个特性。


双字符

意识到三字符系统很笨拙,C99提供了双字符(digraph),可以使用它们来替换某些标准C标点符号。

双字符
双字符 符号 双字符 符号 双字符 符号
<: [ :> ] <% {
%> } %: # %:%: ##

与三字符不同的是,不会替换双引号中的双字符。因此,下面的代码:

%:include 
%:define LIM 100
<%
   int q<:lim:>;
   printf("More to come.:>");
%>
会变成这样
#include 
#define LIM 100
{
   int q[LIM];
   printf("More to come.:>"); // :>是字符串的一部分
}

可选拼写:iso646.h

使用三字符序列可以把||运算符写成??!??!,这看上去比较混乱。C99通过iso646.h头文件提供了可展开运算符的宏。C标准把这些宏称为可选拼写(alternative spelling)。如果包含了iso.646.h头文件,以下代码:

if (x == M1 or x == M2)
   x and_eq 0xFF;
可展开了下面的代码:
if (x == M1 || x == M2)
   x &= 0xFF;


多字节字符

      C标准把多字节字符描述为一个或多个字节的序列,表示源环境或执行环境中的扩展字符集成员。源环境指的是编写源代码的环境,执行环境指的是用户运行已编译程序的环境。这两个环境不同。例如,可以在一个环境中开发程序,在另一个环境中运行该程序。扩展字符集是C语言所需的基本字符集的超集。

      有些实现会提供扩展字符集,方便用户通过键盘输入与基本字符集不对应的字符。这些字符可用于字符串字面量和字符常量中,也可出现在文件中。有些实现会提供与基本字符集等效的多字节字符,可替换三字符和双字符。

例如,德国的一个实现也许会允许用户在字符串中使用日耳曼元音字符:
puts("eins zwei drei vier fünf");
一般而言,程序可使用的扩展字符集团本地化设置而异。

通用字符名(UCN)

      多字节字符可以用在字符串中,但是不能用在标识符中。C99新增了通用字符名(UCN),允许用户在标识名中使用扩展字符集中的字符。系统扩展了转义序列的概念,允许编码ISO/IEC 10646标准中的字符。该标准和统一码(Unicode)关系密切。

     有两种形式的UCN序列。第1种形式是\u hexquard,其中hexquard是一个4位的十六进制数序列(如,\u00F6)。第2种形式是\U hexquardhexquard,如\U0000AC01。因为十六进制每一位上的数对应4位,\u形式可用于16位整数表示的编码,\U形式可用于32位整数表示的编码。

如果系统实现了UCN,而且包含了扩展字符集中所需的字符,就可以在字符串、字符常量和标识符中使用UCN:
wchar_t value\u00F6\u00F8 = L'\u00f6';

统一码和ISO 10646

      统一码为表示不同的字符集提供了一种解决方案,可以根据类型为大量字符和符号制定标准的编号系统。例如,ASCII码被合并为统一码的子集,因此美国拉丁字符(如A~Z)在这两个系统中的编码相同。但是,统一码还合并了其他拉丁字符(如,欧洲语言中使用的一些字符)和其他语言中的字符,包括希腊文、西里尔字母、希伯来文、切罗基文、阿拉伯文、泰文、孟加拉文和形意文字(如中文和日文)。到目前为止,统一码表示的符号超过了110000个,而且仍在发展中。欲了解更多细节,请查阅统一码联合站点:http://www.unicode.org

      统一码为每个字符分配一个数字,这个数字称为代码点(code point)。典型的统一码代码点类似:U-222B。U表示该字符是统一字符,222B是表示该字符的一个十六进制数,在这种情况下,表示积分号。

      国际标准化组织(ISO)组建了一个团队开发ISO 10646和标准编码的多语言文本。ISO 10646团队和统一码团队从1991年开始合作,一直保持两个标准的相互协调。

宽字符

C99为使用宽字符提供更多支持,通过wchar.h和wctype.h库包含了更多大型字符集。这两个头文件把wchar_t定义为一种整型类型,其确切的类型依赖实现。该类型用于储存扩展字符集中的字符,扩展字符集是基本字符集的超集。根据定义,char类型足够处理基本字符集,而wchar_t类型则需要更多位才能储存更大范围的编码值。例如,char可能是8位字节,wchar_t可能是16位的unsigned short。

用L前缀标识宽字符常量和字符字面量,用%lc和%ls显示宽字符数据:
wchar_t wch = L'I';
wchar_t w_arr[200] = L"am wide!";
printf("%lc %ls\n", wch, w_arr);
例如,如果把wchar_t实现为2字节单元,'I'的1字节编码应储存在wch的低位字节。不是标准字符集中的字符可能需要两个字节储存字符编码。例如,可以使用通用字符编码表示超出char类型范围的字符编码:
wchar_t w = L'\u00E2'; /* 16位编码值 */ 内含wchar_t类型值的数组可用于储存宽字符串,每个元素储存一个宽字符编码。编码值为0的wchar_t值是空字符的wchar_t类型等价字符。该字符被称为空宽字符(null wide character),用于表示宽字符串的结尾。
可以使用%lc和%ls读取宽字符:
wchar_t wch1;
wchar_t w_arr[20];
puts("Enter your grade:");
scanf("%lc", &wch1);
puts("Enter your first name:");
scanf("%ls", w_arr);
wchar_t头文件为宽字符提供更多支持,特别是提供了宽字符I/O函数、宽字符转换函数和宽字符串控制函数。例如,可以用fwprintf()和wprintf()函数输出,用fwscanf()和wscanf()函数输入。与一般输入/输出函数的主要区别是,这些函数需要宽字符格式字符串,处理的是宽字符输入/输出流。例如,下面的代码把信息作为宽字符显示:
wchar_t * pw = L"Points to a wide-character string";
int dozen = 12;
wprintf(L"Item %d: %ls\n", dozen, pw);
类似地,还有getwchar()、putwchar()、fgetws()和fputsws()函数。wchar_t头文件定义了一个WEOF宏,与EOF在面向字节的I/O中起的作用相同。该宏要求其值是一个与任何有效字符都不对应的值。因为wchar_t类型的值都有可能是有效字符,所以wchar_t库定义了一个wint_t类型,包含了所有wchar_t类型的值和WEOF的值。

该库中还有与string.h库等价的函数。例如,wcscpy(ws1, ws2)把ws1指定的宽字符串拷贝到ws2指向的宽字符数组中。类似地,wcscmp()函数比较宽字符串,等等。

wctype.h头文件新增了字符分类函数,例如,如果iswdigit()函数的宽字符参数是数字,则返回真;如果iswblank()函数的参数是空白,则返回真。空白的标准值是空格和水平制表符,分别写作L''和L'\t'。

C11标准通过uchar.h头文件为宽字符提供更多支持,为匹配两种常用的统一码格式,定义了两个新类型。第1种类型是char16_t,可储存一个16位编码,是可用的最小无符号整数类型,用于hexquard UCN形式和统一码UTF-16编码方案。
char16_t u = '\u00F6';
第2种类型是char32_t,可储存一个32位编码,最小的可用无符号整数类型。可用于hexquard UCN形式和统一码UTF-32编码方案
char 32_t u = '\U0000AC01';
前缘u和U分别表示char16_t和char32_t字符串。
char16_t ws16[11] = u"Tannh\u00E4user";
char32_t ws32[13] = U"caf\U000000E9 au lait";
注意,这两种类型比wchar_t类型更具体。例如,在一个系统中,wchar_t可以储存32位编码,但是在另一个系统中也许只能储存16位的编码。另外,这两种新类型都与C++兼容。

宽字符和多字节字符

      宽字符和多字节字符是处理扩展字符集的两种不同的方法。例如,多字节字符可能是一个字节、两个字节、三个字节或更多字节,而所有的宽字符都只有一个宽度。多字节字符可能使用移位状态(移位状态是一个字节,确定如何解释后续字节);而宽字符没有移位状态。可以把多字节字符的文件读入使用标准输入函数的普通char类型数组,把宽字节的文件读入使用宽字符输入函数的宽字节数组。

      C99在wchar.h库中提供了一些函数,用于多字节和宽字节之间的转换。mbrtowc()函数把多字节字符转换为宽字符,wcrtomb()函数把宽字符转换为多字节字符。类似地,mbstrtowcs()函数把多字节字符串转换为宽字节字符串,wcstrtombs()函数把宽字节字符串转换为多字节字符串。

      C11在uchar.h库中提供了一些函数,用于多字节和char16_t之间的转换,以及多字节和char32_t之间的转换。

标签: C语言

Powered by emlog  蜀ICP备18021003号-1   sitemap

川公网安备 51019002001593号