Unicode和UTF-8的区别
Unicode和Utf-8的区别
ISO/Unicode组织共同发布能够沟唯一地表示各种语言中的字符标准,通常情况下,我们将一个标准中能够表示的所有字符的集合称为字符集。通常,我们称ISO/Unicode所定义的字符集为Unicode。在Unicode中,每一个字符占据一个码位(Code Point)。Unicode字符集共定义1 114 112个这样的码位,使用0-10FFFF的十六进制数唯一地表示所有的字符。
比较常见的基于Unicode字符集的编码方式有Utf-8,Utf-16,Utf-32(一般人将Utf-16和Unicode混为一谈)
以UTF-8为例,其采用1-6字节的变长编码方式编码Unicode。英文使用1个字节,中文使用3个字节。UTF-8的编码方式如下:
Unicode符号范围(十六进制) UTF-8编码方式(二进制)
0000 0000 - 0000 007F 0xxx xxxx
0000 0080 - 0000 07FF 110x xxxx 10xx xxxx
0000 0800 - 0000 7FFF 1110 xxxx 10xx xxxx 10xx xxxx
0001 0000 - 0010 FFFF 1111 0xxx 10xx xxxx 10xx xxxx 10xx xxxx
Window采用UTF-16编码方式,Mac OS、Linux采用UTF-8编码方式
GB2312字符集:收入6763个汉字和682个非汉字图形字符。而在编码上,采用基于区位码的一种编码方式,采用2个字节表示一个中文字符。
BIG5字符集:常用于繁体中文,共收入13 060个中文字,也采用2字节的方式表示繁体中文。在中国台湾、香港、澳门等地区广泛使用。
由此可见,不同的编码方式对于相同的二进制字符串的解释是不同的。
Unicode还在发展期,Unicode、GB2312以及Big5等多种编码共存的状况可能在以后较长的时间内会持续下去。
C++11中的Unicode支持
C++98标准为了支持Unicode,定义了宽字符wchar_t,标准规定了wchar_t的宽度由编译器实现决定。所以,理论上将,wchar_t的长度可以是8位、16位或32位。例如:Windows它被实现为16位,在Linux上被实现为32位。这样带来的最大问题,程序员写出的包含wchar_t的代码通常不可移植。C++11解决了Unicode类型数据的存储问题。
char16_t:用于存储UTF-16编码的Unicode数据;
char32_t:用于存储UTF-32编码的Unicode数据;
char:用于存储UTF-8编码的Unicode数据
C++11定义了常量字符串前缀,共定义3种:
u8:表示UTF-8编码 u:表示UTF-16编码,使用小写的u将4个十六进制数编码Unicode码位,如'\u4F60' U:表示UTF-32编码,使用大写的U将8个十六进制数编码Unicode码位,如'\U4F60XXXX
L:表示基于宽字符wchar_t
:不加前缀的普通字符串字面量
一旦声明了这些前缀,编译器会在产生代码时按照相应的编码方式存储。
对于Unicode编码字符串的书写,C++11还规定了一些简明的方式,即字面直
在字符串中用'\u'加4个十六进制数编码的Unicode码位(UTF-16)来标识一个Unicode字符。
在字符串中用'\U'加8个十六进制数编码的Unicode码位(UTF-32)来标识一个Unicode字符。
只有非英文才使用'\u'的码位方式来标志,因为英文的Unicode码位都相同。一旦使用了Unicode字符串前缀,这个字符串的类型就确定,仅能放在相应类型的数组中。
C++11在语言层对Unicode进行了支持,但语言层并不是唯一的决定因素,还有编写代码的编辑器、编译器和运行环境的顺序,来看看它们对整个Unicode字符串输出的影响。
以字符char utf8[] = u8"\u4F60"为例
编辑器:
若采用GB2312编码,则源代码中的uft8变量的前两个字节保存的是GB2312编码的中文字“你”
若采用UTF-8编码,则源代码中的uft变量的前三个字节保存的是UTF-8的中文字“你”
编译器:
C++11中的u8前缀保证编译器把utf8变量中的数据以UTF-8的形式产生在目标代码的数据段中,不过编译器也需要设定正确的编码形式(如编译器也设置了GB2312,或者编辑器和编译器都设置了UTF-8),则u8前缀能够正确工作。
输出设备
C++操作符“<<”保证把数据以字节(char)、双字节(char16_t)和四字节(char32_t)的方式输出到输出设备。但输出设备(Linux下的shell,或是Windows下的console)是否能够支持该编码的类型输出,则取决于设备驱动等软件层。
UTF-8:为变长编码,优势是支持更多的码位,也没有大数端和小数端的问题。缺点是不能够通过数组式访问UTF-8。
UTF-16:为定长编码,有LE和BE两种不同的版本。
UTF-8变长的设定是为了在序列化时节省存储空间,而定长的UTF-16编码或UTF-32编码更适合在内存环境中操作。
因此在现有的C++编程中,总是倾向于在I/O读写时采用UTF-8编码(即转化为UTF-8),而在内存中一直操作的是定长的Unicode编码。故编码转换就成了更加常用且不可或缺的功能。
关于Unicode的库支持
mbrtoc16, c16rtomb, mbrtoc32, c32rtomb, 示例代码如下:
#include <cuchar> void A::testUnicodeLib() { setlocale(LC_ALL, "en_US.utf8"); char16_t utf16[] = u"\u4F60\u597D\u554A"; std::size_t in_sz = sizeof(utf16) / sizeof(*utf16); char mbr[sizeof(utf16)*2] = {0}; char *p = mbr; mbstate_t s; for(std::size_t n = 0; n < in_sz; ++n) { int len = c16rtomb(p, utf16[n], &s); if (len == -1) { break; } else { p += len; } } std::cout << "uft-16 -> utf-8: " << mbr << std::endl; }
参考:http://www.cplusplus.com/reference/cuchar/c16rtomb/
还有就是流中设置locale实现编码转换,以及使用wstring_convert模板结合codecvt_utf8、codecvt_utf16、codecvt_utf8_utf16等可以用于字符串转换的模板类。还有to_bytes和from_bytes两个函数,实现wchar_t, char16_t, char32_t到utf-8的字符转换,以及它的逆序from_bytes.
摘自:《深入浅出C++11》