字符编码、Unicode原理、数据流压缩Zlib与Miniz的实现
字符集和字符编码的区别和联系
字符集:多个字符的集合。例如 GB2312 是中国国家标准的简体中⽂字符集,GB2312 收录简化汉字(6763 个)及⼀般符号、序号、数字、拉丁字母、⽇⽂假名、希腊字母、俄⽂字母、汉语拼⾳符号、汉语注⾳字母,共 7445 个图形字符。
字符编码:把字符集中的字符编码为(映射)指定集合中的某⼀对象(例如:⽐特模式、⾃然数序列、电脉冲),以便⽂本在计算机中存储和通过通信⽹络的传递。
字符集和字符编码的关系:
1. 字符集是书写系统字母与符号的集合
2. 字符编码则是将字符映射为⼀特定的字节或字节序列,是⼀种规则。
通常特定的字符集采⽤特定的编码⽅式(即⼀种字符集对应⼀种字符编码(例如:ASCII、 IOS-8859-1、 GB2312、 GBK,都是即表⽰了字符集⼜表⽰了对应的字符编码,但 Unicode 不是,它采⽤现代的模型))
字符集编码的发展
单字节 -> 双字节 -> 多字节
单字节
ASCII(American Standard Code for Information Interchange),128 个字符,⽤ 7 位⼆进制表⽰(00000000-01111111 即
0x00-0x7F),EASCII (Extended ASCII),256 个字符,⽤ 8 位⼆进制表⽰(00000000-11111111 即 0x00-0xFF)。
双字节
当计算机传到了亚洲, 256 个码位就不够⽤了。于是乎继续扩⼤⼆维表,单字节改双字节, 16 位⼆进制数, 65536 个码位。在不同国家和地区⼜出现了很多编码,⼤陆的 GB2312、港台的BIG5、⽇本的 Shift JIS 等等。
注意 65536 个码位这种说法只是理想情况,由于双字节编码可以是变长的,也就是说同⼀个编码⾥⾯有些字符是单字节表⽰,有些字符是双字节表⽰。这样做的好处是, ⼀⽅⾯可以兼容 ASCII,另⼀⽅⾯可以节省存储容量, 代价就是会损失⼀部分码位。
多字节
Unicode 字符集 可以使⽤的编码有三种:
UFT-8:⼀种变长的编码⽅案,使⽤ 1~6 个字节来存储;
UFT-32:⼀种固定长度的编码⽅案,不管字符编号⼤⼩,始终使⽤ 4 个字节来存储;
UTF-16:介于 UTF-8 和 UTF-32 之间,使⽤ 2 个或者 4 个字节来存储, 长度既固定⼜可变。
UTF 是 Unicode Transformation Format 的缩写,意思是“Unicode 转换格式”,后⾯的数字表明⾄少使⽤多少个⽐特位(Bit)来存储字符
Unicode 字符集
Unicode 只是字符集, utf-8、 utf-16、 utf-32 才是真正的编码⽅式。UTF 是 Unicode Transformation Format 的缩写,意思
是“Unicode 转换格式”,后⾯的数字表明⾄少使⽤多少个⽐特位(Bit)来存储字符。
UTF-8
UTF-8: 是⼀种变长字符编码,被定义为将代码点编码为 1 ⾄ 4 个字节,具体取决于代
码点数值中有效位的数量。
注意: UTF-8 不是编码规范,⽽是编码⽅式。下表为 Unicode 值对应的 utf8 需要的字节数量:
Utf8 前缀编码格式
unicode 编码(16 进制)UTF-8 字节流(⼆进制)
000000 - 00007F0xxxxxxx ascii 码
000080 - 0007FF110xxxxx 10xxxxxx
unicode编码转换二进制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 个字节。
UTF-16
UFT-16 ⽐较奇葩,它使⽤ 2 个或者 4 个字节来存储。
对于 Unicode 编号范围在 0~FFFF 之间的字符, UTF-16 使⽤两个字节存储,并且直接存储 Unicode 编号,不⽤进⾏编码转换,这跟 UTF-32 ⾮常类似。
对于 Unicode 编号范围在 10000~10FFFF 之间的字符, UTF-16 使⽤四个字节存储,具体来说就是:将字符编号的所有⽐特位分成两部分,较⾼的⼀些⽐特位⽤⼀个值介于 D800~DBFF 之间的双字节存储,较低的⼀些⽐特位(剩下的⽐特位)⽤⼀个值介于DC00~DFFF 之间的双字节存储。
Unicode 编号范围(⼗六进制)具 体 的 Unicode 编 号
(⼆进制)
UTF-16 编码字节
0000 0000~0000 FFFF xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx2 0001 0000~0010 FFFF yyyy yyyy yyxx xxxx xxxx110110yy yyyyyyyy 110111xx xxxxxxxx4 UTF-32
UTF-32 是固定长度的编码,始终占⽤ 4 个字节,⾜以容纳所有的 Unicode 字符,所以直接存储 Unicode 编号即可,不需要任何编码转换。浪费了空间,提⾼了效率。
UTF BOM 问题
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-16LE FF FE
UTF-16BE FE FF
UTF-32LE FF 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,因为它们没有单字节编码。
UTF-8 缺省不带 BOM
UTF-8 中有⼀字节的情况,这种情况,就没有两端的说法了。⾄于另外的⼆,三,四字节情况,以三字节为例,如果你⼀定要弄出端法,也不是说不可以,⽐如,⼩端法就是“⼩-中-⼤”,⼤端法就是“⼤-中-⼩”。但现实情况是 UTF-8 仅仅采⽤了⼀种端法,就是⼤端法。
字符集相关命令
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 选项的命令⾥⾯都列出来了。
案例:
iconv -f UTF-8 -t -
iconv -f UTF-8 -t -
iconv -f UTF-8 -t -
iconv -f UTF-8 -t -
iconv -f UTF-8 -t -
iconv -f UTF-8 -t -
iconv -f UTF-8 -t -
iconv -f UTF-8 -t -
iconv -f UTF-8 -t -
iconv -f UTF-8 -t -
iconv -f UTF-8 -t -
iconv -f UTF-8 -t -
iconv -f UTF-8 -t -
iconv -f UTF-8 -t -
字符集转换编程
包含头⽂件
#include <iconv.h>
函数: iconv_t iconv_open (const char* tocode, const char* fromcode);
范例: iconv_t cd = iconv_open(“UTF-8”, “UTF-16”);
函数: int iconv_close (iconv_t cd);
范例: iconv_close(cd);
函数: size_t iconv (iconv_t cd, const char* * inbuf, size_t * inbytesleft, char* * outbuf,size_t * outbytesleft);
返回值:返回-1 则说明出现异常,错误码
E2BIG: outbuf 没有⾜够的空间
EILSEQ: 遇到⽆效的多字节序列
EINVAL: 遇到不完整的多字节序列
压缩原理
压缩原理其实很简单,就是出那些重复出现的字符串,然后⽤更短的符号代替,从⽽达到缩短字符串的⽬的。⽐如,有⼀篇⽂章⼤量使⽤"中华⼈民共和国"这个词语,我们⽤"中国"代替,就缩短了 5 个字符,如果⽤"华"代替,就缩短了 6 个字符。事实上,只要保证对应关系,可以⽤任意字符代替那些重复出现的字符串。
本质上,所谓"压缩"就是出⽂件内容的概率分布,将那些出现概率⾼的部分代替成 更 短 的 形 式 。 所 以 , 内 容 越 是 重 复 的⽂ 件 , 就 可 以 压 缩 地 越 ⼩ 。 ⽐ 如 ,"ABABABABABABAB"可以压缩成"7AB"。
相应地,如果内容毫⽆重复, 就很难压缩。极端情况就是,遇到那些均匀分布的随机字符串,往往连⼀个字符都压缩不了。⽐如, 任意排列的 10 个阿拉伯数字(5271839406),就是⽆法压缩的;再⽐如,⽆理数(⽐如 π)也很难压缩。
压缩极限-⾹农极限
∑ log2(1/pn) / n
= log2(1/p1)/n + log2(1/p2)/n + ... + log2(1/pn)/n
(1) 下⾯是⼀个例⼦。假定有两个⽂件都包含 1024 个符号,在 ASCII 码的情况下,它们的长度是相等的,都是 1KB。甲⽂件的内容
50%是 a, 30%b, 20%是 c,⽂本⾥⾯只有abc,则平均每个符号要占⽤ 1.49 个⼆进制位。
0.5*log2(1/0.5) + 0.3*log2(1/0.3) + 0.2*log2(1/0.2) = 1.49
(2) ⽐如每个字节的数值概率是 0~255,均匀分布每个数值出现的概率 1/256,如果⼀段⽂字的字节数值是平均分布,则 pn = 1/256,计算出极限为 8。
Log21/(1/256) = Log2256 = 8
信息熵
数据为何是可以压缩的,因为数据都会表现出⼀定的特性,称为熵。绝⼤多数的数据所表现出来的容量往往⼤于其熵所建议的最佳容量。⽐如所有的数据都会有⼀定的冗余性,我们可以把冗余的数据采⽤更少的位对频繁出现的字符进⾏标记,也可以基于数据的⼀些特性基于字典编码,代替重复多余的短语。
压缩算法
Deflate 压缩算法
LZ77 算法原理
Huffman 算法原理
Deflate 压缩算法
deflate 是 zip 压缩⽂件的默认算法。 其实 deflate 现在不光⽤在 zip ⽂件中, 在 7z,xz 等其他的压缩⽂件中都⽤。 实际上deflate 只是⼀种压缩数据流的算法。 任何需要流式压缩的地⽅都可以⽤。deflate 算法下的压缩器有三种压缩模型:
不压缩数据, 对于已经压缩过的数据, 这是⼀个明智的选择。 这样的数据会会稍稍增加, 但是会⼩于在其上再应⽤⼀种压缩算法。
压缩, 先⽤ LZ77, 然后⽤ huffman 编码。 在这个模型中压缩的树是 Deflate 规范规定定义的, 所以不需要额外的空间来存储这个树。
压缩, 先⽤ LZ77, 然后⽤ huffman 编码。 压缩树是由压缩器⽣成的, 并与数据⼀起存储。数据被分割成不同的块, 每个块使⽤单⼀的压缩模式。 如过压缩器要在这三种压缩模式中相互切换, 必须先结束当前的块, 重新开始⼀个新的块
LZ77 算法原理
LZ77 压缩算法采⽤字典的⽅式进⾏压缩,是⼀个简单但⼗分⾼效的数据压缩算法。其⽅式就是把数据中⼀些可以组织成短语(最长字符)的字符加⼊字典,然后再有相同字符出现采⽤标记来代替字典中的短语,如此通过标记代替多数重复出现的⽅式以进⾏压缩。要理解这种算法, 需先了解 3 个关键词:短语字典,滑动窗⼝和向前缓冲区。
关键词术语
1. 前向缓冲区
每次读取数据的时候, 先把⼀部分数据预载⼊前向缓冲区。为移⼊滑动窗⼝做准备
2. 滑动窗⼝
⼀旦数据通过缓冲区,那么它将移动到滑动窗⼝中,并变成字典的⼀部分。 滑动窗⼝需要预设⼀个定值。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。