Chinaunix首页 | 论坛 | 博客
  • 博客访问: 114995
  • 博文数量: 28
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 202
  • 用 户 组: 普通用户
  • 注册时间: 2014-05-31 21:51
个人简介

諸惡莫做,眾善奉行,自淨其意!

文章分类

全部博文(28)

文章存档

2018年(1)

2017年(3)

2015年(3)

2014年(21)

我的朋友

分类: Python/Ruby

2018-05-29 14:05:19


字符的编码本身就很操蛋,加上python在2.x和3.x处理字符的方式差别也挺大的,这就使得字符的编码问题在python显得尤为蛋疼。为了加以区分理解,个人参考一些资料汇总如下。

首先,把python扔到一边,从操蛋的字符编码讲起。

ASCII编码和非ASCII编码


我们知道,计算机内部,所有信息最终都是一个二进制值。每一个二进制位(bit)有01两种状态,因此八个二进制位就可以组合出256种状态,这被称为一个字节(byte)。也就是说,一个字节一共可以用来表示256种不同的状态,每一个状态对应一个符号,就是256个符号,从0000000011111111。所以字符文本对于计算机而言,也无非是01的组合。计算机处理的基本单位是一个字节,即每8个01的组合才有意义。相对于万能的计算机,我们这些凡人看不懂这些01的组合是什么东东啊,因此就必须有一套规定法则,把这些01的组合和每个字符对应起来,这些规定法则就是所谓的编码系统。
最早的一套字符编码系统是美国制定的,(没办法啊,第一台计算机就是由美国人发现的),美国人起了一个名--ASCII码。美国人的规则是:ASCII码一共规定了128个字符(包括32个不能打印出来的控制符号),比如空格SPACE是32(二进制00100000),大写的字母A是65(二进制01000001),且每个字符只由一个字节表示。前面已经说过,一个字节一共可以用来表示256种不同的状态,所以ASCII码中的每个字符只占用了一个字节的后面7位,最前面的一位统一规定为0
所以对于美国人或者英语为母语的人来说,问题就解决了。比如现在有个文本,里面的内容是这样的:

其对应的二进制字节码是:01100001 01100010 01100011 01100100 00001101 00001010, 为了书写方便,一般用16进制的字节码表示:61 62 20 63 64 0d 0a,用Notepad++软件查看的结果是这样的: 

字符:"a", "b", " "(空格),"c", "d" 对应的分别是61,62,20,63。另外还有两个字节:0d,0a分别表示回车键和换行符,所以文本的总大小为6 bytes。
后来,就像建造巴比伦塔一样,世界各地的都开始使用计算机,但是很多国家用的不是英文,他们的字母里有许多是ASCII里没有的,为了可以在计算机保存他们的文字,他们决定采用 127号之后的空位来表示这些新的字母、符号,还加入了很多画表格时需要用下到的横线、竖线、交叉等形状,一直把序号编到了最后一个状态255。从128 到255这一页的字符集被称。从此之后,贪婪的人类再没有新的状态可以用了,美帝国主义可能没有想到还有第三世界国家的人们也希望可以用到计算机吧!

等中国人们得到计算机时,已经没有可以利用的字节状态来表示汉字,况且有6000多个常用汉字需要保存呢。这个难题我想一定是东北人解决的,因为东北第一定律: “世界上没有什么事是一顿烧烤不能解决的。如果有,那就两顿”。一个字节不行,那我们就用两个啊。我们不客气地规定:一个小于127的字符的意义与原来相同(和ASCII码相同),但两个大于127的字符连在一起时,就表示一个汉字,前面的一个字节(他称之为高字节)从0xA1用到 0xF7(161-247),后面一个字节(低字节)从0xA1到0xFE,这样我们就可以组合出大约7000多个简体汉字了。在这些编码里,我们还把数学符号、罗马希腊的字母、日文的假名们都编进去了,连在 ASCII 里本来就有的数字、标点、字母都统统重新编了两个字节长的编码,这就是常说的”全角”字符,而原来在127号以下的那些就叫”半角”字符了。 中国人民看到这样很不错,于是就把这种汉字方案叫做 “GB2312“。GB2312 是对 ASCII 的中文扩展。

但是中国的汉字太多了,我们很快就就发现有许多人的人名没有办法在这里打出来,特别是某些很会麻烦别人的国家领导人。于是我们不得不继续把 GB2312 没有用到的码位找出来老实不客气地用上。 后来还是不够用,于是干脆不再要求低字节一定是127号之后的内码,只要第一个字节是大于127就固定表示这是一个汉字的开始,不管后面跟的是不是扩展字符集里的内容。结果扩展之后的编码方案被称为 GBK 标准,GBK包括了GB2312 的所有内容,同时又增加了近20000个新的汉字(包括繁体字)和符号。 后来少数民族也要用电脑了,于是我们再扩展,又加了几千个新的少数民族的字,GBK扩成了 GB18030。从此之后,中华民族的文化就可以在计算机时代中传承了。 中国的程序员们看到这一系列汉字编码的标准是好的,于是通称他们叫做 “DBCS“(Double Byte Charecter Set 双字节字符集)。在DBCS系列标准里,最大的特点是两字节长的汉字字符和一字节长的英文字符并存于同一套编码方案里,因此他们写的程序为了支持中文处理,必须要注意字串里的每一个字节的值,如果这个值是大于127的,那么就认为一个双字节字符集里的字符出现了。那时候凡是受过加持,会编程的计算机僧侣 们都要每天念下面这个咒语数百遍: “一个汉字算两个英文字符!一个汉字算两个英文字符……”

因为当时各个国家都像中国这样搞出一套自己的编码标准,结果互相之间谁也不懂谁的编码,谁也不支持别人的编码,连大陆和台湾这样只相隔了150海里,使用着同一种语言的兄弟地区,也分别采用了不同的 DBCS 编码方案——当时的中国人想让电脑显示汉字,就必须装上一个”汉字系统”,专门用来处理汉字的显示、输入的问题,但是那个台湾的愚昧封建人士写的算命程序就必须加装另一套支持 BIG5 编码的什么”倚天汉字系统”才可以用,装错了字符系统,显示就会乱了套!这怎么办?而且世界民族之林中还有那些一时用不上电脑的穷苦人民,他们的文字又怎么办? 真是计算机的巴比伦塔命题啊!正在这时,大天使加百列及时出现了——一个叫 ISO (国际标谁化组织,1947年成立)的国际组织决定着手解决这个问题。 1984年,它发起了 ISO 10646 项目, 名为 “Universal Multiple Octet Coded Character Set”, 又名为通用字符集(英语:Universal Character Set, UCS),简称 UCS。但是,想当英雄永远不止一个。1988年,有个号称统一码联盟(The Unicode Consortium也忙着拯救人民于水深火热之中。起初,UCS 和 unicode 并不兼容, 但这俩项目本身就是为了解决冲突而生,搞出两套有何意义?1991年前后,两个项目的参与者都认识到,世界不需要两个不兼容的字符集。于是,它们开始合并双方的工作成果,并为创立一个单一编码表而协同工作。

Unicode的编码和实现


大概来说,Unicode编码系统可分为编码方式和实现方式两个层次。所谓的编码方式就是规定所有已知语言系统的字符(或是将来有待开发的字符)其同一对应的数字。Unicode使用一个确定的名字和一个叫做码位(code point)的整数来定义一个字符。例如©字符被命名为“copyright sign”并且有一个值为U+00A9(0xA9,十进制169)的码位。为了防止编码空间不够的情况再次出现,Unicode统一码版本定义了17个Unicode字符平面(1基本多文种平面,16个辅助平面),而每平面拥有65536(即2^16)个代码点,因此Unicode码空间总计是17 × 65,536 = 1,114,112, 几乎不可能出现编码空间不够用得情况。虽然编码空间足够大了,但是真正需要用到的占很少的一部分。目前16位统一码字符构成的基本多文种平面即下表的0号平面理论上一共最多可以表示2^16(即65536)个字符,基本满足各种语言的使用。
平面 始末字符值 中文名称 英文名称
0号平面 U+0000 - U+FFFF 基本多文种平面 Basic Multilingual Plane,简称BMP
1号平面 U+10000 - U+1FFFF 多文种补充平面 Supplementary Multilingual Plane,简称SMP
2号平面 U+20000 - U+2FFFF 表意文字补充平面 Supplementary Ideographic Plane,简称SIP
3号平面 U+30000 - U+3FFFF 表意文字第三平面(未正式使用) Tertiary Ideographic Plane,简称TIP
4号平面

13号平面
U+40000 - U+DFFFF (尚未使用)
14号平面 U+E0000 - U+EFFFF 特别用途补充平面 Supplementary Special-purpose Plane,简称SSP
15号平面 U+F0000 - U+FFFFF 保留作为私人使用区(A区) Private Use Area-A,简称PUA-A
16号平面 U+100000 - U+10FFFF 保留作为私人使用区(B区) Private Use Area-B,简称PUA-B

UTF-16(UCS-2
Unicode的实现方式不同于编码方式。一个字符的Unicode码位是确定的。但是在实际储存传输过程中,由于不同系统平台的设计不一定一致,以及出于节省空间的目的,对Unicode编码的实现方式有所不同。Unicode的实现方式称为Unicode转换格式(Unicode Transformation Format,简称为UTF)
最简单粗暴的方式直接使用与Unicode编码一致(仅限于BMP字符)的UTF-16编码(也称UCS-2),每个字符固定占用了两个字节。但是扯淡的是,有些人对字节顺序的理解是不一致的。在麦金塔电脑(Mac)机和个人电脑上,对字节顺序的理解是不一致的。这时同一字节流可能会被解释为不同内容,如某字符为十六进制编码4E59,按两个字节拆分为4E和59,在Mac上读取时是从低字节开始,那么在Mac OS会认为此4E59编码为594E,找到的字符为“奎”,而在Windows上从高字节开始读取,则编码为U+4E59的字符为“乙”。就是说在Windows下以UTF-16编码保存一个字符“乙”,在Mac OS环境下打开会显示成“奎”。此类情况说明UTF-16的编码顺序若不加以人为定义就可能发生混淆,于是在UTF-16编码实现方式中使用了大端序(Big-Endian,简写为UTF-16 BE)小端序(Little-Endian,简写为UTF-16 LE)的概念,以及可附加的字节顺序记号解决方案,目前在PC机上的Windows系统和Linux系统对于UTF-16编码默认使用UTF-16 LE。
下面分贝“乙”字符大小端的编码


”字符大小端的编码这正好跟上面的反过来:


fe ff 表示大端 (天啊,为什么大端不是大数ff在前面啊,太扯淡的规定了),ff fe 表示小端。如果没有附加的字节顺序记号fe ff(或ff ef),“乙”“奎”就会混淆。另外附加的记号,文本大小变成4字节,而不是2字节。

UTF-8
虽然UTF-16(UCS-2)编码帮我们解决了字符集统一的问题,但这并不是没代价的。例如,如果一个仅包含基本7位ASCII字符的Unicode文件,如果每个字符都使用2字节的原Unicode编码传输,其第一字节的8位始终为0,这就造成了比较大的浪费。目前普遍采用的是UTF-8的编码方式,UTF-8 最大的一个特点,就是它是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。
UTF-8 的编码规则很简单,只有二条:
1)对于单字节的符号,字节的第一位设为0,后面7位为这个符号的 Unicode 码。因此对于英语字母,UTF-8 编码和 ASCII 码是相同的。
2)对于n字节的符号(n > 1),第一个字节的前n位都设为1,第n + 1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的 Unicode 码。
下表总结了编码规则,字母x表示可用编码的位。
Unicode符号范围     |        UTF-8编码方式
(十六进制)        |              (二进制)
----------------------+---------------------------------------------
0000 0000-0000 007F | 0xxxxxxx
0000 0080-0000 07FF | 110xxxxx 10xxxxxx
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
跟据上表,解读 UTF-8 编码非常简单。如果一个字节的第一位是0,则这个字节单独就是一个字符;如果第一位是1,则连续有多少个1,就表示当前字符占用多少个字节。
下面,还是以汉字严为例,演示如何实现 UTF-8 编码。严的 Unicode 是4E25(100111000100101),根据上表,可以发现4E25处在第三行的范围内(0000 0800 - 0000 FFFF),因此严的 UTF-8 编码需要三个字节,即格式是1110xxxx 10xxxxxx 10xxxxxx。然后,从严的最后一个二进制位开始,依次从后向前填入格式中的x,多出的位补0。这样就得到了,严的 UTF-8 编码是11100100 10111000 10100101,转换成十六进制就是E4B8A5。
Unicode 与 UTF-8 之间的转换
通过上一节的例子,可以看到严的 Unicode码 是4E25,UTF-8 编码是E4B8A5,两者是不一样的。它们之间的转换可以通过程序实现。
Windows平台,有一个最简单的转化方法,就是使用内置的记事本小程序notepad.exe。打开文件后,点击文件菜单中的另存为命令,会跳出一个对话框,在最底部有一个编码的下拉条。
里面有四个选项:ANSI,Unicode,Unicode big endian和UTF-8。
1)ANSI是默认的编码方式。对于英文文件是ASCII编码,对于简体中文文件是GB2312编码(只针对 Windows 简体中文版,如果是繁体中文版会采用 Big5 码)。
2)Unicode编码这里指的是notepad.exe使用的 UCS-2 编码方式,即直接用两个字节存入字符的 Unicode 码,这个选项用的 little endian 格式。
3)Unicode big endian编码与上一个选项相对应。我在下一节会解释 little endian 和 big endian 的涵义。
4)UTF-8编码,也就是上一节谈到的编码方法。
选择完"编码方式"后,点击"保存"按钮,文件的编码方式就立刻转换好了。

现在开始讲Python中的编码,核心是比较2.X和3.X的区别。一句话概括来说,2.X中的 strunicode对象类型变成了3.X中的 bytesstr 类型,另外3.X还增加了新的数据类型 bytearray.

文本文件(unicode text)和二进制文件(binary data


开始前先简单介绍一下文本文件和二进制文件。大家都知道计算机的存储在物理上是二进制的,所以文本文件与二进制文件的区别并不是物理上的,而是逻辑上的,这两者只是在编码层次上有差异。通俗的说,就是无论文本文件还是二进制文件,在计算机看来仍旧是数字0和1的组合,只是对数字组合的解析的方式不一样。文本文件是基于字符编码的文件,常见的编码有ASCII编码,UNICODE编码等等。二进制文件是基于值编码的文件,你可以根据具体应用,指定某个值是什么意思(这样一个过程,可以看作是自定义编码)。
  • 文本文件与二进制文件的存取   
文本工具打开一个文件的过程是怎样的呢?拿记事本来说,它首先读取文件物理上所对应的二进制比特流,然后按照你所选择的解码方式来解释这个流,然后将解释结果显示出来。一般来说,你选取的解码方式会是ASCII码形式(ASCII码的一个字符是8个比特),接下来,它8个比特8个比特地来解释这个文件流。例如对于这么一个文件流"01000000_01000001_01000010_01000011"(下划线''_'',为了增强可读性手动添加的),第一个8比特''01000000''按ASCII码来解码的话,所对应的字符是字符''A'',同理其它3个8比特可分别解码为''BCD'',即这个文件流可解释成“ABCD”,然后记事本就将这个“ABCD”显示在屏幕上。
事实上,世界上任何东西要与其他东西通信会话,都存在一个既定的协议,既定的编码。人与人之间通过文字联络,汉字“妈”代表生你的那个人,这就是一种既定的编码。但注意到这样一种情况,汉字“妈”在日本文字里有可能是你生下的那个人,所以当一个中国人A与日本B之间用“妈”这个字进行交流,出现误解就很正常的。用记事本打开二进制文件与上面的情况类似。记事本无论打开什么文件都按既定的字符编码工作(如ASCII码),所以当他打开二进制文件时,出现乱码也是很必然的一件事情了,解码和译码不对应嘛。例如文件流''00000000_00000000_00000000_00000001''可能在二进制文件中对应的是一个四字节的整数int 1,在记事本里解释就变成了"NULL_NULL_NULL_SOH"这四个控制符。文本文件的存储与其读取基本上是个逆过程。而二进制文件的存取显然与文本文件的存取差不多,只是编/解码方式不同而已,也不再叙述。
  • 编码(encoding)译码(decoding)
看有个编码的英文资料时,经常会出现encoding和decoding两个单词,经常会把人绕晕。Encoding(编码)是把字符按一定编码方式(如前面说的UTF-8)翻译成成原始字节(二进制)的过程,而decoding(译码)则是把原始字节翻译成字符的过程。简单来说,encode from string to bytes, decode from bytes to string.

字符串类型:Python 2.X vs 3.X


    历史:由于Python创始人在开发初期认知的局限性,其并未预料到python能发展成一个全球流行的语言,导致其开发初期并没有把支持全球各国语言当做重要的事情来做,所以就轻佻的把ASCII当做了默认编码。当后来大家对支持汉字、日文、法语等语言的呼声越来越高时,Python于是准备引入unicode, 但若直接把默认编码改成unicode的话是不现实的,因为很多软件就是基于之前的默认编码ASCII开发的,编码一换,那些软件的编码就都乱了。所以Python 2 就直接 搞了一个新的字符类型,就叫unicode类型,比如你想让你的中文在全球所有电脑上正常显示,在内存里就得把字符串存成unicode类型。时间来到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就是单纯二进制啦。

Python 3.X有三种字符串类型:
str是处理Unicode文本文件(包括ASCII和非ASCII)的,  bytes是用来处理二进制文件(包括文本文件编码成的文件)的,bytearray则是bytes的一种变形。
Python 2.X也有三种类型,但是差别挺大的:str处理的是ASCII的文本文件(不包括非ASCII的)和二进制文件,unicode处理的是Unicode文本文件(包括ASCII和非ASCII)的,和Python 3.X中的str一样,bytearray则是Python 3.X向2.X移植的端口。
可以发现,2.X中的str类型和unicode类型是有重叠的,所以python 3.X重新定义了字符串类型。具体可以看字符‘中’在python 3.X和2.X输出。

3.X运行:‘中’字符是非ASCII的编码,3.X将其定义为str类型,而2.x则会认定为unicode类型。但是,已经不支持直接在字符前加b,字符‘中’被理解为文本文件(前面加u和不加u是一样的),要转换成二进制文件,要指定编码方式才行。

点击(此处)折叠或打开

  1. In [92]: import sys
  2. In [93]: sys.getdefaultencoding()
  3. Out[93]: 'utf-8'
  4. In [94]: a,b='中',u'中'
  5. In [95]: a,b
  6. Out[95]: ('中', '中')
  7. In [96]: type(a),type(b)
  8. Out[96]: (str, str)
  9. In [97]: c = b'中'
  10.   File "", line 1
  11.     c = b'中'
  12.        ^
  13. SyntaxError: bytes can only contain ASCII literal characters.

  14. In [98]: c = a.encode('utf-8')
  15. In [99]: c
  16. Out[99]: b'\xe4\xb8\xad'
  17. In [100]: type(c)
  18. Out[100]: bytes
2.X运行:‘中’字符是非ASCII,2.X认定它为str类型中的二进制类型,其输出不是直接的字符,而是二进制字符串(加b和不加b都是一样的)。前面加u,则转为unicode类型。

点击(此处)折叠或打开

  1. >>> import sys
  2. >>> sys.getdefaultencoding()
  3. 'ascii'
  4. >>> a,b,= '中',u'中',b'中'
  5. >>> a,b,c
  6. ('\xd6\xd0', u'\u4e2d', '\xd6\xd0')
  7. >>> type(a),type(b),type(c)
  8. (<type 'str'>, <type 'unicode'>, <type 'str'>)

Python 3.x 和 2.x脚本运行过程


3.x运行过程:(a)解释器找到代码文件,把代码字符串按文件头定义的编码(python3 默认是utf-8)加载到内存,转成unicode;(b)把代码字符串按照语法规则进行解释(c)所有的变量字符都会以unicode编码声明

点击(此处)折叠或打开

  1. s = '中国'
  2. print(s)
  3. out: 中国
一切都很美好,到这里,我们关于编码的学习按说就可以结束了。但是,如生活一样,美好的表面下,总是隐藏着不尽如人意,上面的utf-8编码之所以能在windows gbk的终端下显示正常,是因为到了内存里python解释器把utf-8转成了unicode , 但是这只是python3, 并不是所有的编程语言在内存里默认编码都是unicode, 比如万恶的python2 就不是, 它的默认编码是ASCII,直接用python 2.x运行上面的脚本会报错(SyntaxError: Non-ASCII character )。想写中文,就必须声明文件头的coding为gbk or utf-8, 声明之后,python2解释器仅以文件头声明的编码去解释你的代码,加载到内存后,并不会主动帮你转为unicode, 也就是说,你的文件编码是utf-8, 加载到内存里,你的变量字符串就也是utf-8, 这意味着什么你知道么?。。。意味着,你以utf-8编码的文件,在windows是乱码。
2.x运行结果是乱码:

点击(此处)折叠或打开

  1. # coding:utf8
  2. s = '中国'
  3. print(s)

  4. out:涓?浗
既然Python2并不会自动的把文件编码转为unicode存在内存里, 那就只能使出最后一招了,你自己人肉转。Py3 自动把文件编码转为unicode必定是调用了什么方法,这个方法就是,decode(译码) 和encode(编码):

点击(此处)折叠或打开

  1. # coding:utf8
  2. s = '中国'
  3. s2 = s.decode('utf-8')
  4. print(s2)
  5. print(type(s2))
  6. s3 = s2.encode('utf-8')
  7. s4 = s2.encode('GBK')
  8. print(s3,type(s3))
  9. print(s4,type(s4))

 out:
  1. 中国
  2. <type 'unicode'>
  3. ('\xe4\xb8\xad\xe5\x9b\xbd', <type 'str'>)
  4. ('\xd6\xd0\xb9\xfa', <type 'str'>)
但是如果我们直接用python 3.x则会出现下面的报错,原因很简单。因为3.x本身就把s看成是字符串,而字符串只能被编码,而不能被译码,所以报错。而2.x之所以没报错,是因为它把s看成是二进制的字节码,字节码就可以被译码成为字符串:

点击(此处)折叠或打开

  1. Traceback (most recent call last):
  2.   File "Python27/test.py", line 3, in <module>
  3.     print(s.decode('utf-8'))
  4. AttributeError: 'str' object has no attribute 'decode'

Python对字符的基本操作 

查看一个字符对应的码位(返回的是十进制的整数),查看某个码位对应的字符:

点击(此处)折叠或打开

  1. In [14]: ord('中')
  2. Out[14]: 20013
  3. In [15]: chr(20013)
  4. Out[15]: '中'
进制间的转换:

点击(此处)折叠或打开

  1. In [65]: a = 20013
  2. In [66]: a
  3. Out[66]: 20013
  4. In [67]: hex(a)
  5. Out[67]: '0x4e2d'
  6. In [68]: oct(a)
  7. Out[68]: '0o47055'
  8. In [69]: bin(a)
  9. Out[69]: '0b100111000101101'
  10. In [70]: int(bin(a),base=2)
  11. Out[70]: 20013
  12. In [71]: int(oct(a),base=8)
  13. Out[71]: 20013
  14. In [72]: int(hex(a),base=16)
  15. Out[72]: 20013
值得指出的是,上面所说的编码或译码过程只有当文本需要存储或传输到内存外部时才会发生。在内存中,Python总是以解码中立的格式(encoding-neutral format)存储解码后的文本字符串,这可能会或可能不会为每个字节使用多个字节字符(python 3.2固定用两个字节表示一个字符,python 3.3和其后的版本则用1,2或4个字节表示一个字符)。所有文本处理都以这种统一的内部格式(即前面说的中立格式)进行,文本存储为的一系列Unicode字符(code point, 码位)。Unicode明确要求我们用字符而不是字节来思考字符串,len()函数返回的也是字符的数目而不是字节的数目。
更详细地说,一个3.X字节的对象bytes实际上是一连串的小整数,每个对象的范围在0到255之间; 索引一个字节返回一个int,切片返回另一个字节,并且运行内置的列表返回一个整数列表,而不是字符。此外,为了方便起见,字节对象被打印为字符串而不是整数。
python 3.x 用一个新的字面形式,b'xxx'(或B'xxx')来创建字节对象。
Python 2.X的u'xxx'和U'xxx'Unicode字符串文字形式在Python 3.0中被删除,因为它们被认为是冗余的 - 在3.X中,正常的字符串就是Unicode。然而,为了同时兼顾向前和向后兼容性,它们在3.3中再次可用,但是本质上加u和不加u是一样的。同样的情况,在Python 2.6和2.7中,为了与3.X的前向兼容性而存在b'xxx'字节字面值,但与'xxx'是一样的,并且字节仅仅是str的同义词。

点击(此处)折叠或打开

  1. In [82]: s,b = 'spam', b'spam'
  2. In [83]: type(s),type(b)
  3. Out[83]: (str, bytes)
  4. In [84]: print(s,b)
  5. spam b'spam'
  6. In [85]: for i in s:print(i,type(i))
  7. s <class 'str'>
  8. p <class 'str'>
  9. a <class 'str'>
  10. m <class 'str'>
  11. In [86]: for j in b:print(j,type(j))
  12. 115 <class 'int'>
  13. 112 <class 'int'>
  14. 97 <class 'int'>
  15. 109 <class 'int'>

参考链接:
字符编码笔记:ASCII,Unicode 和 UTF-8


unicode和ucs的区别
文本文件与二进制文件区别





阅读(1746) | 评论(0) | 转发(0) |
0

上一篇:Python super() 类继承

下一篇:没有了

给主人留下些什么吧!~~