python 之路,致那些年,我们依然没搞明白的编码
本节内容 编码回顾 编码转换 Python的bytes类型 编码回顾在备编码相关的课件时,在知乎上看到一段关于Python编码的回答 这哥们的这段话说的太对了,搞Python不把编码彻底搞明白,总有一天它会猝不及防坑你一把。 不过感觉这哥们的答案并没把编码问题写明白,所以只好亲自动笔了。? 折腾编码问题,有很多次,我以为自已明白了,最终发现,那只不过是自圆其说而已,这一次,终于100%确定,动笔即不再改! 看这篇文章前,你应该已经知道了为什么有编码,以及编码的种类情况
由于每个国家都有自己的字符,所以其对应关系也涵盖了自己国家的字符,但是以上编码都存在局限性,即:仅涵盖本国字符,无其他国家字符的对应关系。应运而生出现了万国码,他涵盖了全球所有的文字和二进制的对应关系,
Unicode 起到了2个作用:
Unicode解决了字符和二进制的对应关系,但是使用unicode表示一个字符,太浪费空间。例如:利用unicode表示“Python”需要12个字节才能表示,比原来ASCII表示增加了1倍。 由于计算机的内存比较大,并且字符串在内容中表示时也不会特别大,所以内容可以使用unicode来处理,但是存储和网络传输时一般数据都会非常多,那么增加1倍将是无法容忍的!!! 为了解决存储和网络传输的问题,出现了Unicode Transformation Format,学术名UTF,即:对unicode中的进行转换,以便于在存储和网络传输时可以节省空间!
总结:UTF 是为unicode编码 设计 的一种 在存储 和传输时节省空间的编码方案。 字符在硬盘上的存储?无论以什么编码在内存里显示字符,存到硬盘上都是2进制。 Shift_JIS编码(日本):
私 0b10001110 0b10000100
は 0b10000010 0b11001101
ks_c_5601-1987编码(韩国):
? 0b10110011 0b10101010
? 0b10110100 0b11000010
TIS-620编码(泰国):
??? 0b10101001 0b11010001 0b10111001
...
decode示例encode 示例记住下图规则 ?如何验证编码转对了呢?1. 查看数据类型,python 2 里有专门的unicode 类型 2. 查看unicode编码映射表 unicode字符是有专门的unicode类型来判断的,但是utf-8,gbk编码的字符都是str,你如果分辨出来的当前的字符串数据是何种编码的呢? 有人说可以通过字节长度判断,因为utf-8一个中文占3字节,gbk一个占2字节 靠上面字节个数,虽然也能大体判断是什么类型,但总觉得不是很专业。 怎么才能精确的验证一个字符的编码呢,就是拿这些16进制的数跟编码表里去匹配。 “路飞学城”的unicode编码的映射位置是 u'u8defu98deu5b66u57ce' ,‘u8def’ 就是‘路’,到表里搜一下。 “路飞学城”对应的GBK编码是'xc2xb7xb7xc9xd1xa7xb3xc7' ,2个字节一个中文,"路" 的二进制 "xc2xb7"是4个16进制,正好2字节,拿它到unicode映射表里对一下, 发现是G0-4237,并不是xc2xb7呀。。。擦。演砸了吧。。 ? 再查下“飞” u98de ,对应的是G0-3749, 跟xb7xc9也对不上。 虽然对不上, 但好xc2xb7 和G0-4237中的第2位的2和第4位的7对上了,“飞”字也是一样,莫非巧合?? 把他们都转成2进制显示试试? 1 1 0 0 0 0 1 0
这个“路”还是跟G0-4237对不上呀,没错, 但如果你把路xc2xb7的每个二进制字节的左边第一个bit变成0试试呢, 我擦,加起来就真的是4237了呀。。难道又是巧合???? 必然不是,是因为,GBK的编码表示形式决定的。。因为GBK编码在设计初期就考虑到了要兼容ASCII,即如果是英文,就用一个字节表示,2个字节就是中文,但如何区别连在一起的2个字节是代表2个英文字母,还是一个中文汉字呢? 中国人如此聪明,决定,2个字节连在一起,如果每个字节的第1位(也就是相当于128的那个2进制位)如果是1,就代表这是个中文,这个首位是128的字节被称为高字节。 也就是2个高字节连在一起,必然就是一个中文。 你怎么如此笃定?因为0-127已经表示了英文的绝大部分字符,128-255是ASCII的扩展表,表示的都是极特殊的字符,一般没什么用。所以中国人就直接拿来用了。? 问:那为什么上面?"xc2xb7"的2进制要把128所在的位去掉才能与unicode编码表里的G0-4237匹配上呢? 这只能说是unicode在映射表的表达上直接忽略了高字节,但真正映射的时候 ,肯定还是需要用高字节的哈。 Python bytes类型在python 2 上写字符串 >> s = "路飞"
>>> print s
路飞
>>> s
'xe8xb7xafxe9xa3x9e'
虽说打印的是路飞,但直接调用变量s,看到的却是一个个的16进制表示的二进制字节,我们怎么称呼这样的数据呢?直接叫二进制么?也可以, 但相比于010101,这个数据串在表示形式上又把2进制转成了16进制来表示,这是为什么呢? 哈,为的就是让人们看起来更可读。我们称之为bytes类型,即字节类型, 它把8个二进制一组称为一个byte,用16进制来表示。? 说这个有什么意思呢? 想告诉你一个事实, 就是,python2的字符串其实更应该称为字节串。 通过存储方式就能看出来, 但python2里还有一个类型是bytes呀,难道又叫bytes又叫字符串? 嗯 ,是的,在python2里,bytes == str , 其实就是一回事 除此之外呢, python2里还有个单独的类型是unicode,把字符串解码后,就会变成unicode >> s
'xe8xb7xafxe9xa3x9e' #utf-8
>>> s.decode('utf-8')
u'u8defu98de' #unicode 在unicode编码表里对应的位置
>>> print(s.decode('utf-8'))
路飞 #unicode 格式的字符
由于Python创始人在开发初期认知的局限性,其并未预料到python能发展成一个全球流行的语言,导致其开发初期并没有把支持全球各国语言当做重要的事情来做,所以就轻佻的把ASCII当做了默认编码。 当后来大家对支持汉字、日文、法语等语言的呼声越来越高时,Python于是准备引入unicode,但若直接把默认编码改成unicode的话是不现实的, 因为很多软件就是基于之前的默认编码ASCII开发的,编码一换,那些软件的编码就都乱了。所以Python 2 就直接 搞了一个新的字符类型,就叫unicode类型,比如你想让你的中文在全球所有电脑上正常显示,在内存里就得把字符串存成unicode类型 >> s = "路飞"
>>> s
'xe8xb7xafxe9xa3x9e'
>>> s2 = s.decode("utf-8")
>>> s2
u'u8defu98de'
>>> type(s2)
时间来到2008年,python发展已近20年,创始人龟叔越来越觉得python里的好多东西已发展的不像他的初衷那样,开始变得臃肿、不简洁、且有些设计让人摸不到头脑,比如unicode 与str类型,str 与bytes类型的关系,这给很多python程序员造成了困扰。 龟叔再也忍不了,像之前一样的修修补补已不能让Python变的更好,于是来了个大变革,Python3横空出世,不兼容python2,python3比python2做了非常多的改进,其中一个就是终于把字符串变成了unicode,文件默认编码变成了utf-8,这意味着,只要用python3,无论你的程序是以哪种编码开发的,都可以在全球各国电脑上正常显示,真是太棒啦! PY3 除了把字符串的编码改成了unicode,还把str 和bytes 做了明确区分, str 就是unicode格式的字符, bytes就是单纯二进制啦。 最后一个问题,为什么在py3里,把unicode编码后,字符串就变成了bytes格式? 你直接给我直接打印成gbk的字符展示不好么?我想其实py3的设计真是煞费苦心,就是想通过这样的方式明确的告诉你,想在py3里看字符,必须得是unicode编码,其它编码一律按bytes格式展示。? 好吧,就说这么多吧。? 最后再提示一下,Python只要出现各种编码问题,无非是哪里的编码设置出错了常见编码错误的原因有:
掌握了编码之前的关系后,挨个排错就好啦
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |