TOP

彻底弄懂UTF-8、Unicode、宽字符、locale(一)
2019-07-08 14:10:48 】 浏览:144
Tags:彻底 弄懂 UTF-8 Unicode 字符 locale

最近使用到了wchar_t类型,所以准备详细探究下,没想到水还挺深,网上的资料大多都是复制粘贴,只有个结论,也没个验证过程。本文记录探究的过程及结论,如有不对请指正。

Unicode、UCS

UCS(Universal Character Set)本质上就是一个字符集。
Unicode的开发结合了国际标准化组织所制定的 ISO/IEC 10646,即通用字符集(
Universal Character Set, UCS)。Unicode 与 ISO/IEC 10646 在编码的运作原理相同,但 The Unicode Standard 包含了更详尽的实现信息、涵盖了更细节的主题,诸如比特编码(bitwise encoding)、校对以及呈现等。摘自(Unicode)
所以也可以简单的理解为,Unicode和UCS等价,都是字符集。

UCS编码的长度是31位,可用4个字节表示,可以表示2的31次方个字符。如果两个字符的高位相同,只有低16位不同,则它们属于同一平面,所以一个平面由2的16次方个字符组成。目前大部分字符都位于第一个平面称为BMP。BMP的编码通常以U+xxxx这种形式表示,其中x是16进制数。
比如中文“你”对应的UCS编码为U+4f60,“好”对应的UCS编码为U+597d。更多中文编码可以在Unicode编码表中查询。

有了UCS编码,任何一个字符在计算机中都最多可以用四个字节来表示,称为码点。

UTF8

现在有了UCS字符集,那么一个字符在计算机中真的要按四个字节(UTF-32)来存储吗?
答案是否定的,一方面每个字符都按四字节来存储非常浪费空间,因为大部分字符都在BMP,只有后16位有效,前16位都是0。另一方面这与c语言不兼容,在c语言中0字节表示字符串的结尾,库函数strlen等函数依赖这一点,如果按UTF-32存储,其中有很多0字节并不表示字符串结尾。

Ken Thompson发明了UTF-8编码,可以很好的解决以上问题。Unicode 和 UTF-8 之间的转换关系表如下:

码点起值 码点终值 字节序列 Byte1 Byte2 Byte3 Byte4 Byte5 Byte6
U+0000 U+007F 1 0xxxxxxx
U+0080 U+07FF 2 110xxxxx 10xxxxxx
U+0800 U+FFFF 3 1110xxxx 10xxxxxx 10xxxxxx
U+10000 U+1FFFFF 4 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
U+200000 U+3FFFFFF 5 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
U+4000000 U+7FFFFFFF 6 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx

第一个字节要么最高位是0(ASCII码),要么最高位都是1,最高位之后的1的个数决定了后面的有多少个字节也属于当前字符编码,例如111110xx,最高位之后还有4个1,表示后面的4个字节属于当前编码。后面的每个字节的最高位都是10,可以和第一个字节区分开来。后面字节的x表示的就是UCS编码。所以UTF-8就像一列火车,第一个字节是车头,包含了后面的哪几个字节也属于当前这列火车的信息,后面的字节是车厢,其中承载着UCS编码。

以中文字符“你”为例,对应的Unicode为"U+4f60",二进制表示为0100 1111 0110 0000。按照表中的规则编码成UTF-8就是11100100 10111101 10100000(0xe4 0xbd 0xa0)。

结论:

Unicode本质是字符集,在这个集合中的任意一个字符都可以用一个四字节来表示。

UTF-8是编码规则,可以通过这个规则将Unicode字符集中任一字符对应的字节转换为另一个字节序列。UTF-8只是编码规则中的一种,其它的编码规则还有UTF-16,UTF-32等。

宽字符类型wchar_t

在介绍宽字符前先了解下locale。因为多字节字符串和宽字符串的转换和locale相关。

locale

什么是locale

区域设置(locale),也称作“本地化策略集”、“本地环境”,是表达程序用户地区方面的软件设定。在linux执行locale可以查看当前locale设置:

ubuntu@VM-0-16-ubuntu:~$ locale
LANG=zh_CN.UTF-8
LANGUAGE=
LC_CTYPE="zh_CN.UTF-8"
LC_NUMERIC="zh_CN.UTF-8"
LC_TIME="zh_CN.UTF-8"
LC_COLLATE="zh_CN.UTF-8"
LC_MONETARY="zh_CN.UTF-8"
LC_MESSAGES="zh_CN.UTF-8"
LC_PAPER="zh_CN.UTF-8"
LC_NAME="zh_CN.UTF-8"
LC_ADDRESS="zh_CN.UTF-8"
LC_TELEPHONE="zh_CN.UTF-8"
LC_MEASUREMENT="zh_CN.UTF-8"
LC_IDENTIFICATION="zh_CN.UTF-8"
LC_ALL=

可以将locale理解为一系列环境变量。locale环境变量值的格式为language_area.charset。languag表示语言,例如英语或中文;area表示使用该语言的地区,例如美国或者中国大陆;charset表示字符集编码,例如UTF-8或者GBK。
这些环境变量会对日期格式,数字格式,货币格式,字符处理等多个方面产生影响。

参考资料:

  1. locale wiki
  2. Environment Variables

如何设置系统默认的locale

修改配置文件/etc/default/locale,比如要将locale设为zh_CN.UTF-8,添加如下语句LANG=zh_CN.UTF-8

locale环境变量有何作用

以LC_TIME为例,该变量会影响strftime()等函数。size_t strftime(char *str, size_t maxsize, const char *format, const struct tm *timeptr)
strftime根据format中定义的格式化规则,格式化结构timeptr表示的时间,并把它存储在str中。

#include <locale.h>
#include <stdio.h>
#include <time.h>

int mai  
		
彻底弄懂UTF-8、Unicode、宽字符、locale(一) https://www.cppentry.com/bencandy.php?fid=49&id=227377

首页 上一页 1 2 3 下一页 尾页 1/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇【NOIP2015普及组】 推销员(纪中.. 下一篇DFS和BFS的比较