21.6 更多字符串处理
最早学习字符串时(第 2 章),我们已经看到,可以用 + 号把两个字符串联接起来,就像这样:
>>> print 'cat' + 'dog'
catdog
现在来看还可以对字符串做哪些处理。
Python 中的字符串实际上都是对象(看到了吧,所有一切都是对象……),而且有自己的方法来完成搜索、分解和结合之类的操作。这些方法都称为字符串方法。刚刚看到的 format() 方法就是一种字符串方法。
分解字符串
有时需要把一个长字符串分解成多个小字符串。通常你会想在字符串的某些特定位置(比如说出现某个字符的地方)进行分解。例如,在文本文件中存储数据时,常见的方法就是将各个项用逗号分隔。所以你可能会得到类似这样一个名字字符串:
>>> name_string = 'Sam,Brad,Alex,Cameron,Toby,Gwen,Jenn,Connor'
假设你想把这些名字放在一个列表中,每一项是一个名字。这就需要在出现逗号的地方分解字符串。完成这项工作的 Python 方法名为 split(),它的用法如下:
>>> names = name_string.split(',')
要指出使用哪个字符作为分解标记,这个方法会返回一个列表,也就是把原来的字符串分解为许多部分。如果打印这个例子的输出,这个长长的名字串会分解为一个列表中的单个列表项:
>>> print names
['Sam','Brad','Alex','Cameron','Toby','Gwen','Jenn','Connor']
>>> for name in names:
print name
Sam
Brad
Alex
Cameron
Toby
Gwen
Jenn
Connor

也可以用多个字符作为分解标记。例如,可以使用 'Toby,' 作为分解标记,这会得到下面的列表:
>>> parts = name_string.split('Toby,')
>>> print parts
['Sam,Brad,Alex,Cameron', 'Gwen,Jenn,Connor']
>>> for part in parts:
print part
Sam,Brad,Alex,Cameron
Gwen,Jenn,Connor
这一次,字符串会分解为两部分:'Toby,' 左侧的所有内容,以及 'Toby,' 右侧的所有内容。注意 'Toby,' 并没有出现在列表中,因为分解标记会被丢掉。
还有一点要知道。如果没有为 Python 指定任何分解标记,它会按空白符(whitespace)分解字符串:
>>> names = name_string.split()
空白符表示所有空格、制表符或换行符。
联接字符串
我们刚才了解了如何把一个字符串分解为较小的部分。那么怎样把两个或多个字符串联接起来构成一个较长的字符串呢?(在第 2 章中)我们已经了解到,可以使用 + 操作符把字符串联接起来。这就像把两个字符串相加,只不过这称为拼接(concatenating)。
联接字符串还有一种方法。可以使用 join() 函数。你要指出你希望把哪些字符串联接起来,另外希望在联接的各部分之间插入什么字符(如果有的话)。这实际上与 split() 正相反。下面是交互模式中完成的一个例子:
>>> word_list = ['My', 'name', 'is', 'Warren']
>>> long_string = ' '.join(word_list)
>>> long_string
'My name is Warren'
我得承认这看起来有些怪异。要联接的各个字符串之间可以插入字符,而且这个字符居然放在 join() 前面。在这里,我们希望每个词之间有一个空格,所以使用了 ''.join()。大多数人可能都没有想到会是这样,不过 Python 的 join() 方法确实要这样使用。
下面这个例子会让人觉得我是只小狗:
>>> long_string = ' WOOF WOOF '.join(word_list)
>>> long_string
'My WOOF WOOF name WOOF WOOF is WOOF WOOF Warren'
换句话说,join() 前面的字符串可以用作粘合剂,把其他字符串联接在一起。
搜索字符串
假设你想为妈妈创建一个程序,获取食谱并在 GUI 中显示。你想在一个位置显示配料,在另一个位置显示做法。假设食谱是这样的。
Chocolate Cake
Ingredients:
2 eggs
1/2 cup flour
1 tsp baking soda
1 lb chocolate
Instructions:
Preheat oven to 350F
Mix all ingredients together
Bake for 30 minutes
假设食谱中的各行都被放在一个列表中,每一行在列表中都是单独的元素。怎么找到“Instructions”(做法)部分呢?Python 提供了两种有用的方法。
startswith() 方法可以指出一个字符串是否以某个字符或某几个字符开头。举个例子最能说明问题。在交互模式中试试下面的例子。>>> name = "Frankenstein"名字 Frankenstein 确实以字母 F 开头,所以第一个结果是 True。名字 Frankenstein 确实以 Frank 开头,所以第二个结果也是 True。不过,名字 Frankenstein 不是以 Flop 开头,所以这一个结果是 False。
>>> name.startswith('F')
True
>>> name.startswith("Frank")
True
>>> name.startswith("Flop")
False
因为 startswith() 方法返回一个 True 或 False 值,所以可以在比较或 if 语句中使用这个方法,比如可以这样:
>>> if name.startswith("Frank"):还有一个类似的方法,名为 endswith(),从这个方法名你应该可以想到它会做什么。
print "Can I call you Frank?"
>>> name = "Frankenstein"现在,回到我们的问题……如果想找到食谱的“Instructions”部分从哪里开始,可以这样做:
>>> name.endswith('n')
True
>>> name.endswith('stein')
True
>>> name.endswith('stone')
False
i = 0这个代码会一直循环,直到找到以“Instructions”开头的一行。应该记得,lines[i] 表示 i 是 lines 的索引。所以要从 lines[0](第 1 行)开始,然后是 lines[1](第 2 行),依此类推。while 循环结束时,i 会等于“Instructions”开头的那一行的索引,这正是你要找的位置。 在字符串中搜索:in 和 index()
while not lines[i].startswith("Instructions"):
i = i + 1
startswith() 和 endswith() 方法可以很好地查找位于字符串开头和末尾位置的内容。不过如果想在一个字符串中间找某个内容呢?
下面假设你有一些包含街道地址的字符串,如下:
657 Maple Lane
47 Birch Street
95 Maple Drive
也许你想找出所有包含 Maple 的地址。这里所有字符串都不是以 Maple 开头或结尾的,但是其中有两个确实包含有单词 Maple。怎么找到它们呢?
实际上,我们已经知道怎么做了。前面讨论列表时(第 12 章),曾经见过可以用这种方法来检查某个元素是否在一个列表中:
if someItem in my_list:
print "Found it!"
这里使用了关键字 in 来检查某个元素是否在列表中。关键字 in 也同样适用于字符串。实际上字符串就是一个字符列表,
所以可以这样做:
>>> addr1 = '657 Maple Lane'
>>> if 'Maple' in addr1:
print "That address has 'Maple' in it."
术语箱
在较大的字符串(如 657 Maple Lane)中查找较小的字符串(如 Maple)时,较小的这个字符串称为子串(substring)。
in 关键字只能指出子串是不是位于你检查的字符串中的某个位置,但没有告诉你它到底在什么位置。要得到这个位置,需要使用 index() 方法。类似于搜索列表,index() 会指出较小串从较大字符串中的哪个位置开始。右面是一个例子。
>>> addr1 = '657 Maple Lane'
>>> if 'Maple' in addr1:
position = addr1.index('Maple')
print "found 'Maple' at index", position
如果运行这个代码,会得到右面的输出:
found 'Maple' at index 4
单词 Maple 从字符串 657 Maple Lane 的位置 4 开始。与列表一样,字符串中字母的索引(或位置)都是从 0 开始,所以 M 位于索引 4。

注意,使用 index() 之前,我们首先用 in 查看子串 Maple 是不是确实在较大的字符串中。这是因为,如果使用了 index(),而你查找的内容不在字符串中,就会得到一条错误消息。先用 in 检查可以杜绝这样的错误。在第 12 章中我们对列表也是这样做的。
删除字符串的一部分
你可能常常希望删除或剥除字符串的某一部分。通常,你希望剥除末尾部分,如换行符或一些多余的空格。Python 提供了一个字符串方法 strip(),完全可以做到这一点。只需要告诉它你想剥除哪一部分,如下:
>>> name = 'Warren Sande'
>>> short_name = name.strip('de')
>>> short_name
'Warren San'
在这里剥除了我名字末尾的 de。如果末尾根本没有 de,那么什么也不会剥除:
>>> name = 'Bart Simpson'
>>> short_name = name.strip('de')
>>> short_name
'Bart Simpson'
如果没有告诉 strip() 要剥除哪一部分,它就会去除所有空白符。前面说过,这包括空格、制表符和换行符。所以,如果想要去除一些多余的空格,就可以这样做:

注意我名字后面多余的空格都已经删除。这里有一点很好:你不需要告诉 strip() 要删除多少个空格,它会删除字符串末尾的所有空白符。
改变大小写
我还要告诉你两种字符串方法。可以使用这两种方法把字符串从大写转换为小写,或者反过来,从小写转换为大写。有时你可能希望比较两个字符串,比如 Hello 和 hello,你想知道它们包含的字母是不是相同(尽管大小写可能不完全一样)。一种办法是让两个字符串中的所有字母都变成小写,然后完成比较。
Python 为此提供了一个字符串方法,名为 lower()。可以在交互模式中试试下面的命令:
>>> string1 = "Hello"
>>> string2 = string1.lower()
>>> print string2
hello
还有一个类似的方法,名为 upper():
>>> string3 = string1.upper()
>>> print string3
HELLO
可以为原来的字符串建立全小写(或全大写)的副本,然后比较这两个副本,看看它们是否相同(忽略大小写)。
你学到了什么
在这一章,你学到了以下内容。
如何调整垂直间隔(添加或删除换行符)。
如何用制表符设置水平间隔。
如果使用格式字符串显示不同的数字格式。
使用格式字符串的两种方法:% 符号和 format() 方法。
如何用 split() 分解字符串和用 join() 联接字符串。
如何使用 startswith()、endswith()、in 和 index()。
如何用 strip() 去除字符串末尾的部分。
如何用 upper() 和 lower() 将字符串转换为全大写或全小写。
测试题
- 如果有两个单独的 print 语句,如下:
print "What is"
print "your name?"
怎样把所有内容都打印在同一行上?
打印时如何增加额外的空行?
实现按列对齐时要使用哪一个特殊打印代码?
使用哪个格式字符串强制按 E 记法打印一个数?
动手试一试
- 编写一个程序,询问一个人的姓名、年龄和最喜欢的颜色,然后打印在一句话里。运行这个程序时应该看到类似这样的结果:
>>> ======================== RESTART ========================
>>>
What is your name? Sam
How old are you? 12
What is your favorite color? green
Your name is Sam you are 12 years old and you like green
还记得第 8 章中的乘法表程序(代码清单 8-5)吗?现在编写这个程序的一个改进版本,使用制表符确保所有内容都能很好地按列对齐。
编写一个程序计算 8 的所有分数(例如,1/8, 2/8, 3/8……直到 8/8),要显示 3 位小数。
