第四章字符串string,bytes

来源:互联网 发布:农村淘宝政策 编辑:程序博客网 时间:2024/05/01 23:00

创建字符串

>>> s = '深入 Python'    ①>>> len(s)               ②9>>> s[0]                 ③'深'>>> s + ' 3'             ④'深入 Python 3'
为了创建一个字符串,将其用引号包围。Python字符串可以通过单引号(')或者双引号(")来定义。内置函数len()可返回字符串的长度,字符的个数。这与获得列表,元组,集合或者字典的长度的函数是同一个。Python中,字符串可以想像成由字符组成的元组。Just like getting individual items out of a list, you can get individual characters out of a string using index notation. 
与取得列表中的元素一样,也可以通过下标记号取得字符串中的某个字符。类似列表,可以使用+操作符来连接(concatenate)字符串。
格式化字符串

Python 3支持把值格式化(format)成字符串。

可以有非常复杂的表达式,最基本的用法是使用单个占位符(placeholder)将一个值插入字符串。

>>> username = 'mark'>>> password = 'PapayaWhip'                           >>> "{0}'s password is {1}".format(username, password)    ①"mark's password is PapayaWhip"
这里包含了很多知识。首先,这里使用了一个字符串字面值的方法调用。字符串也是对象,对象则有其方法。其次,整个表达式返回一个字符串。最后,{0}{1} 叫做替换字段(replacement field),他们会被传递给format()方法的参数替换。
复合字段名
>>> import humansize>>> si_suffixes = humansize.SUFFIXES[1000]      ①>>> si_suffixes['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']>>> '1000{0[0]} = 1{0[1]}'.format(si_suffixes)  ②'1000KB = 1MB'
不需要调用humansize模块定义的任何函数我们就可以抓取到其所定义的数据结构:国际单位制(SI, 来自法语Système International)的后缀列表(以1000为进制)。这一句看上去有些复杂,其实不是这样的。{0}代表传递给format()方法的第一个参数,即si_suffixes。注意si_suffixes是一个列表。所以{0[0]}指代si_suffixes的第一个元素,即'KB'。同时,{0[1]}指代该列表的第二个元素,即:'MB'。大括号以外的内容 — 包括1000,等号,还有空格等 — 则按原样输出。语句最后返回字符串为'1000KB = 1MB'

这个例子说明格式说明符可以通过利用(类似)Python的语法访问到对象的元素或属性

这就叫做复合字段名(compound field names)。以下复合字段名都是“有效的”。

  • 使用列表作为参数,并且通过下标索引来访问其元素(跟上一例类似)
  • 使用字典作为参数,并且通过键来访问其值
  • 使用模块作为参数,并且通过名字来访问其变量及函数
  • 使用类的实例作为参数,并且通过名字来访问其方法和属性
  • 以上方法的任意组合
>>> import humansize>>> import sys>>> '1MB = 1000{0.modules[humansize].SUFFIXES[1000][0]}'.format(sys)'1MB = 1000KB'

下面是描述它如何工作的:

  • sys模块保存了当前正在运行的Python实例的信息。由于已经导入了这个模块,因此可以将其作为format()方法的参数。所以替换域{0}指代sys模块。
  • sys.modules is a dictionary of all the modules that have been imported in this Python instance. The keys are the module names as strings; the values are the module objects themselves. So the replacement field {0.modules} refers to the dictionary of imported modules. sys.modules是一个保存当前Python实例中所有已经导入模块的字典。模块的名字作为字典的键;模块自身则是键所对应的值。所以{0.modules}指代保存当前己被导入模块的字典。
  • sys.modules['humansize']即刚才导入的humansize模块。所以替换域{0.modules[humansize]}指代humansize模块。请注意以上两句在语法上轻微的不同。在实际的Python代码中,字典sys.modules的键是字符串类型的;为了引用它们,我们需要在模块名周围放上引号(比如 'humansize')。但是在使用替换域的时候,我们在省略了字典的键名周围的引号(比如 humansize)。在此,我们引用PEP 3101:字符串格式化高级用法,“解析键名的规则非常简单。如果名字以数字开头,则它被当作数字使用,其他情况则被认为是字符串。”
  • sys.modules['humansize'].SUFFIXES是在humansize模块的开头定义的一个字典对象。 {0.modules[humansize].SUFFIXES}即指向该字典。
  • sys.modules['humansize'].SUFFIXES[1000]是一个si(国际单位制)后缀列表:['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']。所以替换域{0.modules[humansize].SUFFIXES[1000]}指向该列表。
  • sys.modules['humansize'].SUFFIXES[1000][0]即si后缀列表的第一个元素:'KB'。因此,整个替换域{0.modules[humansize].SUFFIXES[1000][0]}最后都被两个字符KB替换。
格式说明符
if size < multiple:    return '{0:.1f} {1}'.format(size, suffix)
{1}会被传递给format()方法的第二个参数替换,即suffix。但是{0:.1f}是什么意思呢?它其实包含了两方面的内容:{0}你已经能理解,:.1f则不一定了。第二部分(包括冒号及其后边的部分)即格式说明符(format specifier),它进一步定义了被替换的变量应该如何被格式化。
格式说明符的允许你使用各种各种实用的方法来修饰被替换的文本,就像C语言中的printf()函数一样。我们可以添加使用零填充(zero-padding),衬距(space-padding),对齐字符串(align strings),控制10进制数输出精度,甚至将数字转换成16进制数输出。
在替换域中,冒号(:)标示格式说明符的开始。“.1”的意思是四舍五入到保留一们小数点。“f”的意思是定点数(与指数标记法或者其他10进制数表示方法相对应)。因此,如果给定size为698.24suffix'GB',那么格式化后的字符串将是'698.2 GB',因为698.24被四舍五入到一位小数表示,然后后缀'GB'再被追加到这个串最后。

其他字符串方法
>>> s = '''Finished files are the re-  ①... sult of years of scientif-... ic study combined with the... experience of years.'''>>> s.splitlines()                     ②['Finished files are the re-', 'sult of years of scientif-', 'ic study combined with the', 'experience of years.']>>> print(s.lower())                   ③finished files are the re-sult of years of scientif-ic study combined with theexperience of years.>>> s.lower().count('f')               ④6

我们可以在Python的交互式shell里输入多行(multiline)字符串。一旦我们以三个引号标记多行字符串的开始,按ENTER键,Python shell会提示你继续这个字符串的输入。连续输入三个结束引号以终止该字符串的输入,再敲ENTER键则会执行该条命令(在当前例子中,把这个字符串赋给变量s)。splitlines()方法以多行字符串作为输入,返回一个由字符串组成的列表,列表的元素即原来的单行字符串。请注意,每行行末的回车符没有被包括进去。lower()方法把整个字符串转换成小写的。(类似地,upper()方法执行大写化转换操作。)count()方法对串中的指定的子串进行计数。是的,在那一句中确实出现了6个字母“f”。
>>> query = 'user=pilgrim&database=master&password=PapayaWhip'>>> a_list = query.split('&')                            ①>>> a_list['user=pilgrim', 'database=master', 'password=PapayaWhip']>>> a_list_of_lists = [v.split('=', 1) for v in a_list]  ②>>> a_list_of_lists[['user', 'pilgrim'], ['database', 'master'], ['password', 'PapayaWhip']]>>> a_dict = dict(a_list_of_lists)                       ③>>> a_dict{'password': 'PapayaWhip', 'user': 'pilgrim', 'database': 'master'}
split()方法使用一个参数,即指定的分隔符,然后根据这个分隔符将串分离成一个字符串列表。此处,分隔符即字符“&”,它还可以是其他的内容。现在我们有了一个字符串列表,其中的每个串由三部分组成:键,等号和值。我们可以使用列表解析来遍历整个列表,然后利用第一个等号标记将每个字符串再分离成两个子串。(理论上,值也可以包含等号标记,如果执行'key=value=foo'.split('='),那么我们会得到一个三元素列表['key', 'value', 'foo']。)最后,通过调用dict()函数Python会把那个包含列表的列表(list-of-lists)转换成字典对象。

字符串的分片slice
>>> a_string = 'My alphabet starts where your alphabet ends.'>>> a_string[3:11]           ①'alphabet'>>> a_string[3:-3]           ②'alphabet starts where your alphabet en'>>> a_string[0:2]            ③'My'>>> a_string[:18]            ④'My alphabet starts'>>> a_string[18:]            ⑤' where your alphabet ends.'
我们可以通过指定两个索引值来获得原字符串的一个“slice”。该操作的返回值是一个新串,依次包含了从原串中第一个索引位置开始,直到但是不包含第二个索引位置之间的所有字符。就像给列表做分片一样,我们也可以使用负的索引值来分片字符串。字符串的下标索引是从0开始的,所以a_string[0:2]会返回原字符串的前两个元素,从a_string[0]开始,直到但不包括a_string[2]如果省略了第一个索引值,Python会默认它的值为0。所以a_string[:18]a_string[0:18]的效果是一样的,因为从0开始是被Python默认的。同样地,如果第2个索引值是原字符串的长度,那么我们也可以省略它。所以,在此处a_string[18:]a_string[18:44]的结果是一样的,因为这个串的刚好有44个字符。这种规则存在某种有趣的对称性。在这个由44个字符组成的串中,a_string[:18]会返回前18个字符,而a_string[18:]则会返回除了前18个字符以外字符串的剩余部分。事实上a_string[:n]总是会返回串的前n个字符,而a_string[n:]则会返回其余的部分,这与串的长度无关。
STRING VS. BYTES
字节即字节;字符是一种抽象。一个不可变(immutable)的Unicode编码的字符序列叫做string

一串由0到255之间的数字组成的序列叫做bytes对象。

>>> by = b'abcd\x65'  ①>>> byb'abcde'>>> type(by)          ②<class 'bytes'>>>> len(by)           ③5>>> by += b'\xff'     ④>>> byb'abcde\xff'>>> len(by)           ⑤6>>> by[0]             ⑥97>>> by[0] = 102       ⑦Traceback (most recent call last):  File "<stdin>", line 1, in <module>TypeError: 'bytes' object does not support item assignment
使用“byte字面值”语法b''来定义bytes对象。byte字面值里的每个字节可以是ascii字符或者是从\x00\xff编码了的16进制数。bytes对象的类型是bytes跟列表和字符串一样,我们可以通过内置函数len()来获得bytes对象的长度。使用+操作符可以连接bytes对象。操作的结果是一个新的bytes对象。连接5个字节的和1个字节的bytes对象会返回一个6字节的bytes对象。一如列表和字符串,可以使用下标记号来获取bytes对象中的单个字节。对字符串做这种操作获得的元素仍为字符串,而对bytes对象做这种操作的返回值则为整数。确切地说,是0–255之间的整数。bytes对象是不可变的;我们不可以给单个字节赋上新值。如果需要改变某个字节,可以组合使用字符串的切片和连接操作(效果跟字符串是一样的),或者我们也可以将bytes对象转换为bytearray对象。
>>> by = b'abcd\x65'>>> barr = bytearray(by)  ①>>> barrbytearray(b'abcde')>>> len(barr)             ②5>>> barr[0] = 102         ③>>> barrbytearray(b'fbcde')
使用内置函数bytearray()来完成从bytes对象到可变的bytearray对象的转换。所有对bytes对象的操作也可以用在bytearray对象上。

有一点不同的就是,我们可以使用下标标记给bytearray对象的某个字节赋值。并且,这个值必须是0–255之间的一个整数




>>> by = b'd'>>> s = 'abcde'>>> by + s                       ①Traceback (most recent call last):  File "<stdin>", line 1, in <module>TypeError: can't concat bytes to str>>> s.count(by)                  ②Traceback (most recent call last):  File "<stdin>", line 1, in <module>TypeError: Can't convert 'bytes' object to str implicitly>>> s.count(by.decode('ascii'))  ③
不能连接bytes对象和字符串。他们两种不同的数据类型。也不允许针对字符串中bytes对象的出现次数进行计数,因为串里面根本没有bytes。字符串是一系列的字符序列。也许你是想要先把这些字节序列通过某种编码方式进行解码获得字符串,然后对该字符串进行计数?可以,但是需要显式地指明它。Python 3不会隐含地将bytes转换成字符串,或者进行相反的操作。好巧啊…这一行代码刚好给我们演示了使用特定编码方式将bytes对象转换成字符串后该串的出现次数。
>>> a_string = '深入 Python'         ①>>> len(a_string)9>>> by = a_string.encode('utf-8')    ②>>> byb'\xe6\xb7\xb1\xe5\x85\xa5 Python'>>> len(by)13>>> by = a_string.encode('gb18030')  ③>>> byb'\xc9\xee\xc8\xeb Python'>>> len(by)11>>> by = a_string.encode('big5')     ④>>> byb'\xb2`\xa4J Python'>>> len(by)11>>> roundtrip = by.decode('big5')    ⑤>>> roundtrip'深入 Python'>>> a_string == roundtripTrue
a_string是一个字符串。它有9个字符。by是一个bytes对象。它有13个字节。它是通过a_string使用utf-8编码而得到的一串字节序列。by还是一个bytes对象。它有11个字节。它是通过a_string使用GB18030编码而得到的一串字节序列。此时的by仍旧是一个bytes对象,由11个字节组成。它又是一种完全不同的字节序列,我们通过对a_string使用Big5编码得到。roundtrip是一个字符串,共有9个字符。它是通过对by使用Big5解码算法得到的一个字符序列。并且,从执行结果可以看出,roundtripa_string是完全一样的。