分类: C/C++
2012-04-26 12:29:57
C把环境分为两种:编译期的translation environment,它使用的字符集为source character set;和运行期的execution environment,它使用的字符集为execution character set。在许多C编译器里,这两个字符集是相同的,但对于有些不同的编译器,它们把源码中的字符串和字符常量转换为相应的执行字符集的相应元素。
基本的源码和执行字符集都包含以下字符:
The letters of the Latin alphabet
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
a b c d e f g h i j k l m n o p q r s t u v w x y z
The decimal digits
0 1 2 3 4 5 6 7 8 9
The following 29 punctuation marks
! " # % & ' ( ) * + , - . / : ; < = > ? [ \ ] ^ _ { | } ~
The five whitespace characters
Space, horizontal tab, vertical tab, new line, and form feed
基本执行字符集也包含不可打印的字符:字符串的终止符null; alert; backspace; 和carriage return。在字符常量和字符串里表示它们的方法:\0 for the null character,\a for alert, \b for backspace, and \r for carriage return。
不同的C实现字符编码都不同。C语言本身只保证以下条件成立:
基本字符集里的每个字符都必须可以用一个字节表示;
null字符是一个所有位都为0的字节。
0之后的每个十进制数字的编码都比前一个数字的编码大一。
多字节字符(Multibyte Character)
多字节字符和宽字符都可以表示基本字符集之外的国际字符。多字节字符仍使用char类型,但它所表示的内容根据上下文不同,可能由一个字节(基本字符集)或多个字节组成。比如:
char *s = "淘to米mmy挣";
printf("|| %s ||", s);
看看上面字符串的字节编码:
{230[?],183[?],152[?],116[t],111[o],231[?],177[?],179[?],109[m],109[m],121[y],230[?],140[?],163[?]}
可以发现只占用7字节(小于128的值)都是单字节,其它的都是多字节。注意strlen的返回值是占用的字节数,而不是字符数。
多字节字符使用得字符串的操作变得复杂,但是它适合于存在于文件里。
宽字符(wide character)
每个宽字符的位宽度都是一样的。stddef.h里定义了wchar_t(wide character type),它可能是一个int的typedef(即utf-32)。C标准没有规定对Unicde字符集的支持,但许多实现都使用Unicode转换格 式(Unicode Transformation Format)UTF-16和UTF-32来作为宽字符。多字节的UTF-8字符集可以表示所有的Unicode字符,它使用1到4个字节来表示一个字 符。Unicode标准和ISO/IEC 10646标准相当一致,且是许多之前存在的字符集的超级,包括7位的AscII编码。实现Unicdoe标准的wchar_t类型至少是16或32位 宽。
比如:
setlocale(LC_ALL, "zh_CN.utf8");
wchar_t wc = L'淘';
printf("%lc", wc);
%lc或%C、%ls或%S分别是宽字符和宽字符串的格式(%C、%S不是C标准的一部分,而是XSI扩展)。setlocale是必须的,它启用了运行期的中文编码。
还可以使用wprintf(wchar.h)来打印:
wprintf(L"%lc", wc);
注 意,printf和wprintf不能混合使用,否则会有打印不出事情发生。比如,先使用printf,再使用wprintf,那么wprintf不会输 出任何东西。这是因为每个文件流(FILE stream),包括stdout,都有一个关联的orientation(byte或wide)。第一个在这个流上进行的操作会设置这个 orientation,后续与这个orientation冲突的操作都会导至undefined的行为。
mbstowcs和wcstombs可以在多字节字符串和宽字符串之间转换。
wchar_t ws[100];
mbstowcs(ws, s, 100);
wprintf(L"|| %S ||", ws);
回过头来,看一下宽字符串的内存布局。
0x6dd8[淘],0x0074[t],0x006f[o],0x7c73[米],0x006d[m],0x006d[m],0x0079[y],0x6323[挣]
相应的,我们可以用16进制来定义宽字符的值(\x逃脱符):
wchar_t wc = L'\x6DD8';
至此,多字节字符和宽字符之间的区别应该很明显了。
Universal Character Names
C9X引入了UCN的概念。它是一个与实现无关的Unicode字符编码,使用\uXXXX或\UXXXXXXXX的格式。例如:
wchar_t wc = L'\u6DD8';与wchar_t wc = L'\U00006DD8'等价。
小写u后必须是正好4位16进制编码,而大写U后必须是正好8位16进制编码。
Digraph | 等价于 |
---|---|
<: | [ |
:> | ] |
<% | { |
%> | } |
%: | # |
%:%: | ## |
Trigraph | 等价于 |
---|---|
??( | [ |
??) | ] |
??< | { |
??> | } |
??= | # |
??/ | \ |
??! | | |
??' | ^ |
??- | ~ |
例如:
int a??(10??) = ??< 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ??>;
等价于
int a<:10:> = <% 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 %>;
等价于
int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
参考: