C++11与Unicode及使用标准库进行UTF-8、UTF-16、UCS2、UCS4/UTF-32编码转换

zt https://blog.poxiao.me/p/unicode-character-encoding-conversion-in-cpp11/

Unicode

Unicode是计算机领域的一项行业标准,它对世界上绝大部分的文字的进行整理和统一编码,Unicode的编码空间可以划分为17个平面(plane),每个平面包含2的16次方(65536)个码位。17个平面的码位可表示为从U+0000到U+10FFFF,共计1114112个码位,第一个平面称为基本多语言平面(Basic Multilingual Plane, BMP),或称第零平面(Plane 0)。其他平面称为辅助平面(Supplementary Planes)。基本多语言平面内,从U+D800到U+DFFF之间的码位区段是永久保留不映射到Unicode字符,所以有效码位为1112064个。最新的版本是Unicode 6.3发布于2013年9月30日。

Unicode的编码方式

对于被Unicode收录的字符其编码是唯一且确定的。但是Unicode的实现方式(出于传输、存储、处理或向后兼容的考虑)却有不同的几种,其中最流行的是UTF-8、UTF-16、UCS2、UCS4/UTF-32等,细分的话还有大小端的区别。

UTF-8(8-bit Unicode Transformation Format)

UTF-8是一种变长编码,对于一个Unicode的字符被编码成1至4个字节。Unicode编码与UTF-8的编码的对应关系如下表

Unicode编码 UTF-8编码(二进制)
U+0000 – U+007F 0xxxxxxx
U+0080 – U+07FF 110xxxxx 10xxxxxx
U+0800 – U+FFFF 1110xxxx 10xxxxxx 10xxxxxx
U+10000 – U+10FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

其中绝大部分的中文用三个字节编码,部分中文用四个字节编码,举例如下:

Unicode 字符 UTF-8编码
U+0041 A 0×41
U+7834 0xE7 0xA0 0xB4
U+6653 0xE6 0×99 0×93
U+2A6A5 ?(四个龍) 0xF0 0xAA 0x9A 0xA5

优点:
1.向后兼容ASCII编码;
2.没有字节序(大小端)的问题适合网络传输;
3.存储英文和拉丁文等字符非常节省存储空间。
缺点:
1.变长编码不利于文本处理;
2.对于CJK等文字比较浪费存储空间。

UTF-16(16-bit Unicode Transformation Format)

UTF-16也是一种变长编码,对于一个Unicode字符被编码成1至2个码元,每个码元为16位。

基本多语言平面(码位范围U+0000-U+FFFF)

在基本多语言平面内的码位UTF-16编码使用1个码元且其值与Unicode是相等的(不需要转换)。举例如下

Unicode 字符 UTF-16(码元) UTF-16 LE(字节) UTF-16 BE(字节)
U+0041 A 0×0041 0×41 0×00 0×00 0×41
U+7834 0×7834 0×34 0×78 0×78 0×34
U+6653 0×6653 0×53 0×66 0×66 0×53
辅助平面(码位范围U+10000-U+10FFFF)

在辅助平面内的码位在UTF-16中被编码为一对16bit的码元(即32bit,4字节),称作代理对(surrogate pair)。组成代理对的两个码元前一个称为前导代理(lead surrogates)范围为0xD800-0xDBFF,后一个称为后尾代理(trail surrogates)范围为0xDC00-0xDFFF。UTF-16辅助平面代理对与Unicode的对应关系如下表

Lead \ Trail 0xDC00 0xDC01 0xDFFF
0xD800 U+10000 U+10001 U+103FF
0xD801 U+10400 U+10401 U+107FF
0xDBFF U+10FC00 U+10FC01 U+10FFFF

举例如下

Unicode 字符 UTF-16(码元) UTF-16 LE(字节) UTF-16 BE(字节)
U+2A6A5 ? 0xD869 0xDEA5 0×69 0xD8 0xA5 0xDE 0xD8 0×69 0xDE 0xA5

优点:
1.绝大部分的文字都可以用两个字节编码,对于CJK文字是比较节省空间的;
2.文本处理比UTF-8方便得多。
缺点:
1.存储和传输需要考虑字节序的问题;
2.不兼容ASCII。

UCS2(2-byte Universal Character Set)

UCS2是一种定长编码,编码范围为0×0000-0xFFFF。在基本多语言平面内与UTF-16是等价。UCS2没有类似于UTF-16中代理对的概念,所以对于0xD869 0xDEA5会识别成两个字符。因为是定长编码,所以文本处理很方便。缺点是不能表示全部的Unicode字符。

UCS4(4-byte Universal Character Set)/UTF-32(32-bit Unicode Transformation Format)

UCS4/UTF-32是一种定长编码,使用1个32bit的码元,其值与Unicode编码值相等。举例如下:

Unicode 字符 UTF-32(码元) UTF-32 LE(字节) UTF-32 BE(字节)
U+0041 A 0×00000041 0×41 0×00 0×00 0×00 0×00 0×00 0×00 0×41
U+7834 0×00007834 0×34 0×78 0×00 0×00 0×00 0×00 0×78 0×34
U+6653 0×00006653 0×53 0×66 0×00 0×00 0×00 0×00 0×66 0×53
U+2A6A5 ? 0x0002A6A5 0xA5 0xA6 0×02 0×00 0×00 0×02 0xA6 0xA5

优点是编码定长容易进行文本处理,缺点是浪费存储空间及存在字节序的问题。

C++11对Unicode的支持

C++11对Unicode提供了语言级别和库级别的支持。

USL(Unicode String Literals)

USL是C++11对Unicode提供的语言级别的支持。在C++11之前C++中有个wchar_t的类型用于存储宽字符(Wide-Character)。你可以写下面这样的代码,

1
2
3
wchar_t wc = L '中' ;
wchar_t wc_array[] = L "破晓的博客" ;
std::wstring wstr = L "破晓的博客" ;

以L开头的字符(或字符串)字面量称为WCSL(Wide-Character String Literals)。本意大概也是用来提供Unicode支持的,可惜标准没有规定这个的实现,wchar_t及其字面量是实现相关的。比如
1.在windows平台下sizeof(wchar_t)为2,而在linux平台下sizeof(wchar_t)为4;
2.在windows平台下宽字符(或字符串)字面量使用UTF-16编码,linux平台下使用UTF-32编码。
导致了下面这段代码在windows下编译时会报错,而在linux下可以编译通过。

1
wchar_t wc = L '?' ; // U+2A6A5

导致下面这段代码的输出结果不同,windows下输出为2而linux下的输出为1。

1
2
std::wstring wstr = L "?" ;
std::cout << wstr.size() << std::endl;

C++11新增了char16_t(至少16位)和char32_t(至少32位)以及USL允许下面这样的代码

1
2
3
4
5
6
7
8
9
10
11
// utf-8
char u8_array[] = u8 "破晓的博客" ;
std::string u8_str = u8 "破晓的博客" ;
// utf-16
char16_t u16_c = u '中' ;
char16_t u16_array[] = u "破晓的博客" ;
std::u16string u16_str = u "破晓的博客" ;
// ucs4
char32_t u32_c = U '?' ;
char32_t u32_array[] = U "破晓的博客"
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值