【字符编码】Unicode字符集与字符编码方式

字符集和字符编码的区别和联系

  • 字符集:多个字符的集合。例如 GB2312 是中国国家标准的简体中文字符集,GB2312 收录简化汉字(6763 个)及一般符号、序号、数字、拉丁字母、日文假名、希腊字母、俄文字母、汉语拼音符号、汉语注音字母,共 7445 个图形字符。
  • 字符编码:把字符集中的字符编码为(映射)指定集合中的某一对象(例如:比特模式、自然数序列、电脉冲),以便文本在计算机中存储和通过通信网络的传递

字符集和字符编码的关系

  1. 字符集是书写系统字母与符号的集合
  2. 字符编码则是将字符映射为一特定的字节或字节序列,是一种规则。

通常特定的字符集采用特定的编码方式(即一种字符集对应一种字符编码(例如:ASCII、IOS-8859-1、GB2312、GBK,都是即表示了字符集又表示了对应的字符编码,但 Unicode 不是,它采用现代的模型))

字符集编码的发展

单字节 -> 双字节 -> 多字节

(1) 单字节

ASCII(American Standard Code for Information Interchange), 128 个字符, 用 7 位二进制表示(00000000-01111111 即 0x00-0x7F);EASCII(Extended ASCII),256 个字符,用 8 位二进制表示(00000000-11111111 即 0x00-0xFF)。

当计算机传到了欧洲,国际标准化组织在 ASCII 的基础上进行了扩展,形成了 ISO-8859标准,跟 EASCII 类似,兼容 ASCII,在高 128 个码位上有所区别。但是由于欧洲的语言环境十分复杂,所以根据各地区的语言又形成了很多子标准,ISO-8859-1、ISO-8859- 2、ISO-8859-3、……、ISO-8859-16。

(2) 双字节

当计算机传到了亚洲,256 个码位就不够用了。于是乎继续扩大二维表,单字节改双字节,16 位二进制数,65536 个码位。在不同国家和地区又出现了很多编码,大陆的GB2312、港台的 BIG5、日本的 Shift JIS 等等。

注意 65536 个码位这种说法只是理想情况,由于双字节编码可以是变长的,也就是说同一个编码里面有些字符是单字节表示,有些字符是双字节表示。这样做的好处是,一方面可以兼容 ASCII,另一方面可以节省存储容量,代价就是会损失一部分码位。

GBK(Chinese Internal Code Specification 汉字内码扩展规范)是 GB2312 的扩展(gbk 编码能够用来同时表示繁体字和简体字),按理说都属于双字节编码,码位是一样的,根本谈不上扩展,但实际上是预留空间在起作用。比如下图为 GBK 的编码空间,GBK/1、GBK/2 是 GB2312 的区域,GBK/3、GBK/4、GBK/5 是 GBK 的区域,红色是用户自定义域,白色可能就是由于变长编码损失的区域了。

支持国际标准 ISO/IEC10646-1 和国家标准 GB13000-1 中的全部中日韩汉字。GBK 字符集中所有中文字符和全角符号占 2 个字节,字母和半角符号占一个字节。 没有特殊的编码方式,习惯称呼 GBK 编码。一般在国内,汉字较多时使用。

在这里插入图片描述
(3) 多字节

当互联网席卷了全球,地域限制被打破了,不同国家和地区的计算机在交换数据的过程中,就会出现乱码的问题,即对同一组二进制数据,不同的编码会解析出不同的字符。

UNICODE 字符集国际标准字符集,它将世界各种语言的每个字符定义一个唯一的编码,以满足跨语言、跨平台的文本信息转换。有多个编码方式,分别是 UTF-8,UTF-16,UTF-32 编码

Unicode 字符集 可以使用的编码有三种:

  • UFT-8:一种变长的编码方案,使用 1~6 个字节来存储;
  • UFT-32:一种固定长度的编码方案,不管字符编号大小,始终使用 4 个字节来存储;
  • UTF-16:介于 UTF-8 和 UTF-32 之间,使用 2 个或者 4 个字节来存储,长度
    既固定又可变

UTF 是 Unicode Transformation Format 的缩写,意思是“Unicode 转换格式”,后面的数字表明至少使用多少个比特位(Bit)来存储字符

Unicode字符集的编码方式

汉字 Unicode 编码范围 https://www.qqxiuzi.cn/zh/hanzi-unicode-bianma.php

Unicode 只是字符集,utf-8、utf-16、utf-32 才是真正的编码方式

unicode不是存储格式,是字符的唯一编码,用于根据这个编码找到显示字体位图文件,然后显示出字体到屏幕的坐标点。

utf8/utf16/utf32/gbk是存储格式,存储的文件格式是这几种的其中一种,是对unicode的不同编码方法。

Unicode 字符集 可以使用的编码有三种:

  1. UFT-8:一种变长的编码方案,使用 1~6 个字节来存储;
  2. UTF-16:介于 UTF-8 和 UTF-32 之间,使用 2 个或者 4 个字节来存储,长度既固定又可变。
  3. 一种固定长度的编码方案,不管字符编号大小,始终使用 4 个字节来存储;

UTF 是 Unicode Transformation Format 的缩写,意思是“Unicode 转换格式”,后面的数字表明至少使用多少个比特位(Bit)来存储字符

UTF-8

UTF-8: 是一种变长字符编码,被定义为将代码点编码为 1 至 4 个字节,具体取决于代码点数值中有效位的数量。

下表为 Unicode 值对应的 utf8 需要的字节数量。
Utf8 前缀编码格式

unicode 编码(16 进制)UTF-8 字节流(二进制)
000000 - 00007F0xxxxxxx ascii 码
000080 - 0007FF110xxxxx 10xxxxxx
000800 - 00FFFF1110xxxx 10xxxxxx 10xxxxxx
01 0000 - 10 FFFF11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

范例:计算 Unicode 码使用 utf8 进行表示:
例如:汉字 【严】的 Unicode 码是 4E25 转换成二进制就是 01001110 00100101 共 15 位,根据上表可知使用 UTF-8 字符编码后占 3 个字节,因此前 3 位是 1,第 4 位(n+1 位)是 0,后面两个字节中每个字节的前两位都是 10,即 1110 xxxx 10 xxxxxx 10xxxxxx。填充进去后就变成了 1110 0100 10 111000 10 100101 共计 24 位占 3 个字节。

GBK 内码、Unicode 查询:http://www.mytju.com/classcode/tools/encode_gb2312.asp

UTF-16

UFT-16 比较奇葩,它使用 2 个或者 4 个字节来存储。

  1. 对于 Unicode 编号范围在 0~FFFF 之间的字符,UTF-16 使用两个字节存储,并且直接存储 Unicode 编号,不用进行编码转换,这跟 UTF-32 非常类似。
  2. 对于 Unicode 编号范围在 10000~10FFFF 之间的字符,UTF-16 使用四个字节存储,具体来说就是:将字符编号的所有比特位分成两部分,较高的一些比特位用一个值介于 D800~DBFF 之间的双字节存储,较低的一些比特位(剩下的比特位)用一个值介于 DC00~DFFF 之间的双字节存储。

在这里插入图片描述

UTF-32

UTF-32 是固定长度的编码,始终占用 4 个字节,足以容纳所有的 Unicode 字符,所以直接存储 Unicode 编号即可,不需要任何编码转换。浪费了空间,提高了效率。

UTF BOM 问题

UTF 是 Unicode Transformation Format 的缩写,意思是“Unicode 转换格式”,后面的数字表明至少使用多少个比特位(Bit)来存储字符

BOM(Byte Order Mark)字节序(字节顺序的标识),其实就是用大端(BE)还是小端(LE)。

比如 UTF-16BE 和 UTF-16LE:

  • UTF-16BE,其后缀是 BE 即 big-endian,大端的意思。大端就是将高位的字节放在低地址表示。
  • UTF-16LE,其后缀是 LE 即 little-endian,小端的意思。小端就是将高位的字节放在高地址表示。
  • UTF-16,没有指定后缀,即不知道其是大小端,所以其开始的两个字节表示该字节数组是大端还是小端。即 FE FF 表示大端,FF FE 表示小端。

UTF 在文件中的存储。UTF 格式在文件中总有固定文件头

UTF 编码Byte Order Mark
UTF-8EF BB BF
UTF-16LEFF FE
UTF-16BEFE FF
UTF-32LEFF FE 00 00
UTF-32BE00 00 FE FF

注:UTF-8 缺省不带 BOM

如“汉”字在文件中的存储(不包括头):
在这里插入图片描述

小结

程序打开一个文件时怎么识别用的是 UTF-8 还是 UTF-16?
答:是否有标志做标志,在文件的开头几个字节就是标志.
➢ EF BB BF 表示 UTF-8
➢ FE FF 表示 UTF-16BE
➢ FF FE 表示 UTF-16LE
➢ 00 00 FE FF 表示 UTF32-BE
➢ FF FE 00 00 表示 UTF32-LE

注意:只有 UTF-8 兼容 ASCII,UTF-32 和 UTF-16 都不兼容 ASCII,因为它们没有单字节编码。

查看完整的 Unicode 字符集,以及各种编码方式:https://unicode-table.com/cn/
Unicode 和 UTF 编码转换:https://www.qqxiuzi.cn/bianma/Unicode-UTF.php

UTF-8 缺省不带 BOM
UTF-8 中有一字节的情况,这种情况,就没有两端的说法了。至于另外的二,三,四字节情况,以三字节为例,如果你一定要弄出端法,也不是说不可以,比如,小端法就是“小-中-大”,大端法就是“大-中-小”。但现实情况是 UTF-8 仅仅采用了一种端法,就是大端法。

字符集相关命令

Unicode 和 UTF 编码转换:https://www.qqxiuzi.cn/bianma/Unicode-UTF.php

file

命令:
file:查看文件的编码方式 (分析结果做一个参考)

范例:

# file -i chatset.cpp
chatset.cpp: text/x-c++; charset=utf-8
# file -i chatset-gbk2312.cpp 
chatset-gbk2312.cpp: text/x-c++; charset=iso-8859-1
iconv

iconv 命令是用来转换文件的编码方式的,比如它可以将 UTF8 编码的转换成 GB18030的编码,反过来也行。Linux 下的 iconv 开发库包括 iconv_open,iconv_close,iconv 等 C 函数,可以用来在 C/C++程序中很方便的转换字符编码

语法:
iconv -f encoding [-t encoding] [inputfile]…

选项:
-f encoding :把字符从 encoding 编码开始转换。
-t encoding :把字符转换到 encoding 编码。
-l :列出已知的编码字符集合
-o file :指定输出文件
-c :忽略输出的非法字符
-s :禁止警告信息,但不是错误信息
–verbose :显示进度信息
-f 和-t 所能指定的合法字符在-l 选项的命令里面都列出来了。

  1. 查看支持的格式 iconv -l, 这里节选部分格式
UTF-8, UTF-16, UTF-16BE, UTF-16LE, UTF-32, UTF-32BE, UTF-32LE, 
UTF8, UTF16, UTF16BE, UTF16LE, UTF32, UTF32BE, UTF32LE GB2312 
GBK ISO-8859-1
  1. 转换各种格式
iconv -f UTF-8 -t UTF-8 utf8.txt -o UTF-8.txt
iconv -f UTF-8 -t UTF8 utf8.txt -o UTF8.txt
iconv -f UTF-8 -t UTF-16 UTF-8.txt -o UTF-16.txt
iconv -f UTF-8 -t UTF-16BE UTF-8.txt -o UTF-16BE.txt
iconv -f UTF-8 -t UTF-16LE UTF-8.txt -o UTF-16LE.txt
iconv -f UTF-8 -t UTF16 UTF-8.txt -o UTF16.txt
iconv -f UTF-8 -t UTF16BE UTF-8.txt -o UTF16BE.txt
iconv -f UTF-8 -t UTF16LE UTF-8.txt -o UTF16LE.txt
iconv -f UTF-8 -t UTF-32 UTF-8.txt -o UTF-32.txt
iconv -f UTF-8 -t UTF-32BE UTF-8.txt -o UTF-32BE.txt
iconv -f UTF-8 -t UTF-32LE UTF-8.txt -o UTF-32LE.txt
iconv -f UTF-8 -t GB2312 UTF-8.txt -o GB2312.txt
iconv -f UTF-8 -t GBK UTF-8.txt -o GBK.txt
iconv -f UTF-8 -t ISO-8859-1 UTF-8.txt -o ISO-8859-1.txt

字符集转换编程

iconv文档地址:http://www.gnu.org/software/libiconv/documentation/libiconv-1.13/

#include <iconv.h>
iconv_t iconv_open (const char* tocode, const char* fromcode);

int iconv_close (iconv_t cd);

size_t iconv (iconv_t cd, const char* * inbuf, size_t * inbytesleft, char* * outbuf, 
size_t * outbytesleft); 
返回值:返回-1 则说明出现异常,错误码
	E2BIG:outbuf 没有足够的空间
	EILSEQ:遇到无效的多字节序列
	EINVAL:遇到不完整的多字节序列

字符集应用案例

  1. mysql 字符集设置

部分汉字在 mysql 使用 utf8 字符时写入出现异常,或者读取出现异常。比如𤋮(xī),其和存在区别。𤋮(xī)在 utf8 模式下需要 4 个字节表示。

  • MySQL 的“utf8 ”实际上不是真正的 UTF-8, “utf8”只支持每个字符最多 3 个字节。 而真正的 utf8 至少要支持 4 个字节。
  • MySQL 一直没有修复这个 bug,他们在 2010 年发布了一个叫作"utf8mb4"的字符集。 MySQL 的"utf8mb4"是真正的"UTF-8"。
  1. nginx 配置字符集

conf 文件需要加入 charset utf-8;

  1. redis 命令行时出现中文乱码

单纯以 redis-cli 启动命令行,在 set 中文 value 是再 get 出现乱码,比如:
在这里插入图片描述
附加–raw 参数启动即可,即是 redis-cli --raw 启动命令行:
在这里插入图片描述

字符集在线工具集合

GBK 内码查询

http://www.mytju.com/classcode/tools/encode_gb2312.asp

完整的 Unicode 字符集

https://unicode-table.com/cn/

Unicode 和 UTF 编码转换

https://www.qqxiuzi.cn/bianma/Unicode-UTF.php

汉字字符集编码查询

https://www.qqxiuzi.cn/bianma/zifuji.php