Unicode
于 utf-8
以下文章来自知乎回答
Unicode 是「字符集」
UTF-8 是「编码规则」
其中:
字符集:为每一个「字符」分配一个唯一的 ID(学名为码位 / 码点 / Code Point)
编码规则:将「码位」转换为字节序列的规则(编码/解码 可以理解为 加密/解密 的过程)
广义的 Unicode 是一个标准,定义了一个字符集以及一系列的编码规则,即 Unicode 字符集和 UTF-8、UTF-16、UTF-32 等等编码……
Unicode 字符集为每一个字符分配一个码位,例如「知」的码位是 30693,记作 U+77E5(30693 的十六进制为 0x77E5)。
UTF-8 顾名思义,是一套以 8 位为一个编码单位的可变长编码。会将一个码位编码为 1 到 4 个字节:
U+ 0000 ~ U+ 007F : 0XXXXXXX
U+ 0080 ~ U+ 07FF : 110XXXXX 10XXXXXX
U+ 0800 ~ U+ FFFF : 1110XXXX 10XXXXXX 10XXXXXX
U+10000 ~ U+1FFFF : 11110XXX 10XXXXXX 10XXXXXX 10XXXXXX
根据上表中的编码规则,之前的「知」字的码位 U+77E5 属于第三行的范围:
7 7 E 5
0111 0111 1110 0101 二进制的 77E5
--------------------------
1110XXXX 10XXXXXX 10XXXXXX 模版(上表第三行,X的个数为 4 6 6)即讲77E5的 0111011111100101按长度4 6 6 截取
0111 011111 100101 二进制的 77E5 (unicode统一用两个字节byte即16位bit来表示所有字符)
11100111 10011111 10100101 代入模版
E 7 9 F A 5
# 帅的 Unicode编码16进制为 5E05
5 E 0 5
0101 1110 0000 0101 二进制的 5E05
--------------------------
1110XXXX 10XXXXXX 10XXXXXX 模版(上表第三行,X的个数为 4 6 6)即讲77E5的 0111011111100101按长度4 6 6 截取
0101 111000 000101 二进制的 77E5 (unicode统一用两个字节byte即16位bit来表示所有字符)
11100101 10111000 10000101 代入模版
E 5 B 8 8 5
这就是将 U+77E5 按照 UTF-8 编码为字节序列 E79FA5 的过程。反之亦然。
unicod与utf-8
一个叫 ISO(国际标谁化组织)的国际组织决定着手解决各地区编码混乱的问题。他们采用的方法很简单:废了所有的地区性编码方案,重新搞一个包括了地球上所有文化、所有字母和符号 的编码!他们打算叫它”Universal Multiple-Octet Coded Character Set”,简称 UCS, 俗称 “unicode“。
unicode开始制订时,计算机的存储器容量极大地发展了,空间再也不成为问题了。于是 ISO 就直接规定必须用两个字节,也就是16位来统一表示所有的字符,对于ASCII里的那些“半角”字符,unicode包持其原编码不变,只是将其长度由原来的8位扩展为16位,而其他文化和语言的字符则全部重新统一编码。由于”半角”英文符号只需要用到低8位,所以其高8位永远是0,因此这种大气的方案在保存英文文本时会多浪费一倍的空间。
这时候,从旧社会里走过来的程序员开始发现一个奇怪的现象:他们的 strlen 函数靠不住了,一个汉字不再是相当于两个字符了,而是一个!是的,从unicode开始,无论是半角的英文字母,还是全角的汉字,它们都是统一的”一个字符“!同时,也都是统一的”两个字节“,请注意”字符”和”字节”两个术语的不同,“字节”是一个8位的物理存贮单元,而“字符”则是一个文化相关的符号。在unicode中,一个字符就是两个字节。一个汉字算两个英文字符的时代已经快过去了。
unicode同样也不完美,这里就有两个的问题,一个是,如何才能区别unicode和ascii?计算机怎么知道三个字节表示一个符号,而不是分别表示三个符号呢?第二个问题是,我们已经知道,英文字母只用一个字节表示就够了,如果unicode统一规定,每个符号用三个或四个字节表示,那么每个英文字母前都必然有二到三个字节是0,这对于存储空间来说是极大的浪费,文本文件的大小会因此大出二三倍,这是难以接受的。
unicode在很长一段时间内无法推广,直到互联网的出现,为解决unicode如何在网络上传输的问题,于是面向传输的众多 UTF(UCS Transfer Format)标准出现了,顾名思义,UTF-8就是每次8个位传输数据,而UTF-16就是每次16个位。UTF-8就是在互联网上使用最广的一种unicode的实现方式,这是为传输而设计的编码,并使编码无国界,这样就可以显示全世界上所有文化的字符了。UTF-8最大的一个特点,就是它是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度,当字符在ASCII码的范围时,就用一个字节表示,保留了ASCII字符一个字节的编码做为它的一部分,注意的是unicode一个中文字符占2个字节,而UTF-8一个中文字符占3个字节)。从unicode到utf-8并不是直接的对应,而是要过一些算法和规则来转换。
Unicode符号范围 | UTF-8编码方式
(十六进制 ) | (二进制,1-4个字节,每个字节8位) 这里的二进制再用两位的16进制表示,见上面
—————————————————————–
0000 0000 - 0000 007F | 0xxxxxxx //这里007F刚好就是127,是ascII码包括的的所有字符
0000 0080 - 0000 07FF | 110xxxxx 10xxxxxx
0000 0800 - 0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
0001 0000 - 0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
base64编码
Base64
是网络上最常见的用于传输 8Bit
字节码的编码方式之一,Base64
就是一种基于 64
个可打印字符来表示二进制数据的方法。Base64
编码是从二进制到字符的过程.
普通字符串转 base64
首先清晰一个概念: 内存1个字节占8位即 1byte = 8bit
;
Base64
要求把每三个8Bit
的字节转换为四个6Bit
(为什么是 bBit
呢,因为6位二进制最大 111111
等于 63
,刚好 64
个字符) 的字节(3*8 = 4*6 = 24
),然后把6Bit
再添两位高位0
,组成四个 8Bit
的字节,也就是说,转换后的字符串理论上将要比原来的长 1/3
。
转换的时候,将三个byte
的数据,先后放入一个24bit
的缓冲区中,先来的byte
占高位。数据不足3byte
的话,缓冲区中剩下的Bit
用0
补足。如果最后剩下两个输入数据,在编码结果后加1
个=
;如果最后剩下一个输入数据,编码结果后加2
个=
;如果没有剩下任何数据,就什么都不要加,这样才可以保证资料还原的正确性。
转码过程例子:
3*8=4*6
内存1个字节占8位
转前: s 1 3
先转成ascii:对应 115 49 51
2进制: 01110011 00110001 00110011
6个一组(4组) 011100110011000100110011
然后才有后面的 011100 110011 000100 110011
然后计算机是8位8位的存数 6不够,自动就补两个高位0了
所有有了 高位补0
科学计算器输入 00011100 00110011 00000100 00110011
得到 28 51 4 51
查对下照表 c z E z
转前: a
先转成ascii: 97(十进制,和用utf-8表示的16进制的 61是一样的)
2进制: 01100001
数据不足,补足(3*8) 01100001 00000000 00000000
6个一组(4组) 011000 010000 000000 000000
高位补0凑够8位 00011000 00010000 00000000 00000000
转十进制得到 24 16
查base64对照表 Y Q
最后补两个=得到 YQ==
如果最后剩下两个输入数据,在编码结果后加`1`个`=`;如果最后剩下一个输入数据,编码结果后加`2`个`=`
转前: 帅
先转成utf-8: e5 b8 85 ##可通过let buf = Buffer.from("帅")获得utf-8编码,为16进制
2进制: 11100101 10111000 10000101
6个一组(4组) 111001 011011 100010 000101
高位补0凑够8位 00111001 00011011 00100010 00000101
转十进制得到 57 27 34 5
查base64对照表 5 b i F ## 可通过buf.toString("base64")来验证
其他base64
相关
url
地址要先转码后再转base64
.
点击查看base64的理解
转换方法;
!function(W){
W.Base64 = {
utf8ToBase64:function (str){
return btoa(unescape(encodeURIComponent(str)));
},
base64ToUtf8: function(str){
return decodeURIComponent(escape(atob(str)));
}
}
}(window);
网友评论