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码位,如'\U4F60XXXXL:表示基于宽字符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》
原文链接: https://www.cnblogs.com/gwzz/p/14018660.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/205318

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月12日 下午10:11
下一篇 2023年2月12日 下午10:11

相关推荐