通过Python学习字符串编码
- 字符串的编码和解码
- 到底什么是编码,什么是解码?
- 字符编码标准
- 什么是Unicode?
- 历史发展
- Unicode 与 UTF 的关系
- UTF32与USC-4, UTF16与USC-2
- 有些的地方也说Unicode是具体的编码?
- python字符串编码
字符串的编码和解码
到底什么是编码,什么是解码?
在了解什么是编码
,解码
之前,我们得先来了解一下什么是字符(character)
和字节流(byte streams)
- 字符是我们可以识别看懂的文字
- 字节流则是字符按某种编码格式编码后的二进制存储格式,是用于给计算机存储和识别的
那么什么是编码,什么又是解码呢?
- 将字符转换为字节流的过程,我们称之为编码
- 将字节流转换为字符的过程,我们称之为解码
总的来说就是,把人类所使用的这些字符集转换为计算机所能理解的二级制码,这个过程就是编码,他的逆过程称为解码。
字符编码标准
什么是Unicode?
Unicode是统一码,万国码,单一码,是计算机科学领域的一项行业标准。它包括字符集,编码方案等。Unicode是为了解决传统字符编码方案的局限性而产生的。它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言,跨平台进行文本转换,处理的要求。@百度百科
历史发展
字符串也是一种常见的普通数据类型,每种编程语言都有,只不过字符串相比其他数据类型而言,还带有一个编码问题。
阶段一:
- 我们知道计算机是只能处理数字的,既只认识
0
,1
。 所以当我们的基本数据类型字符串要被计算机处理时,就必须将字符转换成二进制的数字格式。 - 我们也知道计算机是美国发明的,而美国是一个英语系的国家,所以在考虑计算机的问题时,主要也是站在英语系文字的角度去考虑这个问题,所以在早期,只有127个英文字母和一些简单的符号才能被计算机所识别, 因为英文所需要的字符不多,所以所有的字符串对应到计算机的存储格式都只有一个字节,而一个字节(8位)能表示256种字符,一个字节表示还有剩余的空间。而字符和二进程存储的映射则由一份编码表来维护,也就是我们所知道的
ASCII
编码。 - 此阶段只用了8位的低位空间,既00000000~01111111, 也即是0~127
阶段二:
- 随着计算机被迅速的推广和使用,欧洲非英语国家的计算机大牛就发现,这一套美国设计的字符集Too Young Too Simple, 很多其他拉丁字符语言文字根本不够用呀。于是大牛们就在ASCII占用一个字节内存的剩余空间位置又拓展出来了一些字符,填满256, 补充了
ASCII
编码 - 此阶段又补充了高位空间,利用了10000000~11111111, 既128~255的剩余位置,一个字节的空间已经被完全的利用完了。
阶段三
- 随着使用计算机的国家越来越多,已经不再局限什么西欧国家了。中文,韩文,日本以及各种未知文都需要一个字符映射的存储呀。当初ASCII也是Too Young Too Simple, 一个字节怎么能够用呢。我大中华文化博大精深,单单是中文就不够存储了,怎么办怎么办?于是中国国家标准总局发布了一套《信息交换用汉字编码字符集》的国家标准,其标准号就是GB 2312—1980。这个字符集共收入汉字6763个和非汉字图形字符682个,采用两个字节对字符集进行编码,并向下兼容ASCII编码方式。再后来生僻字、繁体字及日韩汉字也被纳入字符集,就又有了后来的GBK字符集及相应的编码规范,GBK编码规范也是向下兼容GBK2312的
- 当然中国有
GBK
格式,韩国有EUC-KR
,日本也有Shift_JIS
。总之每个国家都出现了一系列的字符编码格式,可谓是百家争鸣,但又相互不兼容…
阶段四
-
随着各个国家出现了各种的编码格式,也出现了越来越多互相不兼容的情况,不同的语言字符编码值相同,但却代表不同的字符,例如韩文编码EUC-KR中“한국어”的编码值正好是汉字编码GBK中的“茄惫绢”。因此如果同一份文本文件,拷贝到互不兼容的不同语言编码格式的计算机上,就很有可能出现乱码。所以此时的人们就会想 “我们能不能定义一个超大的字符集,它可以容纳全世界所有的文字字符,再对它们统一进行编码,让每一个字符都对应一个不同的编码值,从而就不会再有乱码了”
-
如果说 “各个国家都在为自己文字独立编码” 是百家争鸣,那么 “建立世界统一的字符编码” 则是一统江湖,谁都想来做这个武林盟主。早前就有两个机构试图来做这个事:
(1) 国际标准化组织(ISO),他们于1984年创建ISO/IEC JTC1/SC2/WG2工作组,试图制定一份“通用字符集”(Universal Character Set
,简称UCS
),并最终制定了ISO 10646
标准。
(2) 统一码联盟,他们由Xerox、Apple等软件制造商于1988年组成,并且开发了Unicode标准(The Unicode Standard
) -
1991年前后,两个项目的参与者都认识到,世界不需要两个不兼容的字符集。于是,它们开始合并双方的工作成果,并为创立一个单一编码表而协同工作。 从Unicode 2.0开始,Unicode采用了与ISO 10646-1相同的字库和字码;ISO也承诺,ISO 10646将不会替超出U+10FFFF的UCS-4编码赋值,以使得两者保持一致。两个项目仍都独立存在,并独立地公布各自的标准。不过由于
Unicode
这一名字比较好记,因而它使用更为广泛
简单小结
- 总之呢,从一开始128到256的ASCII编码,再到各种编码格式的百家争鸣,再到Unicode的统一江湖。我们也可以简单的知道了字符串编码的历史和发展。Unicode的出现就是为了让大家不要再为使用那种编码格式而烦恼
- Unicode虽然出现了,但是Unicode编码规范仅仅是字符码位的定义,属于字符集,不属于编码。所以为了更好的采用和实现Unicode字符集,后面也就出现了以UTF8,UTF16,UTF32为代表的字符串UTF编码格式,可以针对Unicode字符串对字符串进行编码和解码
- 当然不管编码格式怎么发展,都要记住一点ASCII对待西方拉丁文字而言是具有必要意义的,比如GBK,UTF8都是向下兼容ASCII编码格式的
详细可以查看一下三篇的文章奥
- 给妹子讲python-S01E07字符编码历史观-从ASCII到Unicode - @作者:张雨萌-酱油哥
- 细说:Unicode, UTF-8, UTF-16, UTF-32, UCS-2, UCS-4 - @作者:Malecrab
- 字符串和编码 - @作者:廖雪峰
Unicode 与 UTF 的关系
@百度百科
Unicode是统一码,万国码,单一码,是计算机科学领域的一项行业标准。它包括字符集,编码方案等。Unicode是为了解决传统字符编码方案的局限性而产生的(从上面历史发展就可以看到)。它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言,跨平台进行文本转换,处理的要求。
-
从广义上来说,Unicode包括Unicode字符集和编码方案等。既Unicode字符集(字符与二进制编码的映射),以及实际编码方案
UTF8,UTF16,UTF32
都属于Unicode的范畴;从狭义的说,Unicode也被通常指为是Unicode字符集
。实际上不管是Unicode字符集
还是编码方案,它都属于Unicode。所以当某人说某种语言的默认字符编码为Unicode,那么该字符编码可能是UTF8,16,32
的任意一种奥~ -
狭义上的
Unicode
通常指的是Unicode字符集
规范,定义了所有字符的码位。而实际的字符编码方案则是UTF编码
,Unicode字符集
和UTF8
的关系,我们可以理解为一个是规范,一个是具体的实现者。既UTF8是Unicode字符集
的具体编码方案。举个粟子,假如'中'
字符在Unicode字符集的码位是789
,那么就要'中'
字符的 “代号: 789” 要转成怎么样的二进制数值在计算机中存储呢?以几个字节的方式存储呢?如果是UTF8
可能是1~4
字节,UTF16
可能是2~4
个字节,UTF32
就是4
个字节了。同样的Unicode码位,不同的编码方案,就有不同的存储方式。('中’的码位是随便说的哈) -
既Unicode字符集定义了具体字符的 “代号” ,而不同的UTF编码方案则决定了这些 “代号” 在内存中以多大的大小去存储
UTF32与USC-4, UTF16与USC-2
引用至@细说:Unicode, UTF-8, UTF-16, UTF-32, UCS-2, UCS-4 - @作者:Malecrab
utf32与usc-4
在Unicode与ISO 10646合并之前,ISO 10646标准为“通用字符集”(UCS)定义了一种31位的编码形式(即UCS-4),其编码固定占用4个字节,编码空间为0x00000000~0x7FFFFFFF(可以编码20多亿个字符)。
UCS-4有20多亿个编码空间,但实际使用范围并不超过0x10FFFF,并且为了兼容Unicode标准,ISO也承诺将不会为超出0x10FFFF的UCS-4编码赋值。由此UTF-32编码被提出来了,它的编码值与UCS-4相同,只不过其编码空间被限定在了0~0x10FFFF之间。因此也可以说:UTF-32是UCS-4的一个子集
.
utf16与usc-2
除了UCS-4,ISO 10646标准为“通用字符集”(UCS)定义了一种16位的编码形式(即UCS-2),其编码固定占用2个字节,它包含65536个编码空间(可以为全世界最常用的63K字符编码,为了兼容Unicode,0xD800-0xDFFF之间的码位未使用)。例:“汉”的UCS-2编码为6C49。
但俩个字节并不足以正真地“一统江湖”(a fixed-width 2-byte encoding could not encode enough characters to be truly universal),于是UTF-16诞生了,与UCS-2一样,它使用两个字节为全世界最常用的63K字符编码,不同的是,它使用4个字节对不常用的字符进行编码。UTF-16属于变长编码
UTF8, UTF16 , UTF32的区别
UTF8,16,32
都属于Unicode字符集的UTF编码方案 。
- 他们的不同之处是对待同一个Unicode字符编码需要多大的内存空间去存储。比如字符
'A'
, UTF8只需要1
个字节,UTF16则需要2
个字节,UTF32则需要4
个字节。不同的内存空间需求,就造成了不同的场景需要。需要的内存空间越小,在字符传输,存储,处理上性能就更加优秀。但世界上字符这么多,并不是所有字符都能由8位(一字节)的空间大小去存储的。所以才诞生了UTF8,16,32各种编码方案,这些编码方案也各有各的优势和通行场景。
UTF8:
- 存在单字节编码(所以兼容ASCII编码)
- 属于变长存储编码,最少1字节,最多4字节
- 相比UTF16,32而言,没有字节序的问题
UTF16:
- 半固长存储编码,大部分字符以2字节存储,偏颇生词使用4字节存储,既2~4字节
- 有字节序问题 ,有大端小端之分
UTF32
- 固长存储编码,所有字符都以4字节存储
- 因为固长,所以便于处理,属于空间换时间的手段
- 有字节序问题 ,有大端小端之分
区别表格:
对比 | UTF-8 | UTF-16 | UTF-32 | UCS-2 | UCS-4 |
---|---|---|---|---|---|
编码空间 | 0-10FFFF | 0-10FFFF | 0-10FFFF | 0-FFFF | 0-7FFFFFFF |
最少编码字节数 | 1 | 2 | 4 | 2 | 4 |
最多编码字节数 | 4 | 4 | 4 | 2 | 4 |
是否依赖字节序 | 否 | 是 | 是 | 是 | 是 |
有些的地方也说Unicode是具体的编码?
为什么?
- 为什么有一些地方会说计算机内存中使用的是Unicode编码,内存读取外部UTF8编码文件时,需要将UTF8转换为Unicode格式。对外展示的时候,又需要将unicode转换成utf8格式,这样的说法准确吗?
下文都提到了unicode编码与utf编码的转换
解释
- 这种说法准确,但是具有很大的误导性,可以说是windows历史遗留下来的坑。通常我们所说的windows内存默认编码
unicode
, 实际并不是"unicode"
本身, 而是utf-16le
编码,既window内存的实际默认编码是utf-16le
。然而国内有一些博客和文章都没有明说出这一点,也没有区分这一点,导致大量博客的转载或是XXX, 最后约定大于规定,就造成一定的概念混乱(所以有的时候还是建议直接看英文原文文章会更加容易理解)
为什么windows要采用utf16le呢,而不统一采用utf8
-
Windows身上的“历史原因”,在于Unicode标准初生的时候,字符码其实是16位,那个时候的UTF-16就能直接保存Unicode字符码。于是Windows就直接将自己使用的UTF-16 LE编码命名为“Unicode",这在当时是名符其实的。但是后来西方人尴尬地发现如果把中国人故纸堆里的罕用字,各种小语种的文字都收进去的话,16位65536个码位仍然是不够用的。Unicode升级成了32位,出现了字符码突破16位的字符,UTF-16从定长码变成了不定长码,对于Windows来说,这就很坑爹了。尽管字符码在16位以内的字符,UTF-16编码仍然保持不变,但还是很坑爹 来源知乎 - @作者:farta
-
曾经的
utf16le
是2字节(16位)为主,而utf8
从1~4字节的形式都有,因为格式的多样,对计算机的处理其实并没有这么的友好,毕竟如果大部分甚至所有字符都是一样的字节格式(比如16位)会更具有通用性,所以当初windows内存的默认编码是utf16le
。而为什么外部文件系统常用utf8
呢? 也正因为utf8
格式多样,从2字节到4字节都有,对待不同的字符串采用不同的格式进行存储,可以最大的节省空间(尤其是西方文字)。所以通常在传输和文件存储方面,utf8
式的unicode编码格式使用的更加广泛。也就会出现文件存储以utf8格式,被内存读取时,又要将utf8
转换为utf16le
python字符串编码
python3默认的字符串编码就是unicode, utf-8就是unicode字符码位的具体二进制存储格式
# windows 10
import sys
import locale
print(sys.getdefaultencoding()) # 系统默认编码,不要把系统以为是操作系统,这里可以理解成python3的编译器本身
print(locale.getdefaultlocale()) # 本地默认编码,这个才是操作系统的编码,cp936也就是gbk
print(sys.getfilesystemencoding())
print(sys.stdin.encoding)
print(sys.stdout.encoding)
"""
output:
utf-8
('zh_CN', 'cp936')
utf-8
UTF-8
UTF-8
"""
- 我们可以看到Python3的编译器,文件系统,输入输出的字符串默认编码都是unicode编码的utf8格式