
2.9 字符串处理
字符串在编程语言中十分重要,作者认为最主要的原因是:它是程序与代码设计者之间沟通的桥梁。一个优秀的软件工程师不应该把程序写得难懂并且无法维护,而应该在实现算法基本功能和保证性能的基础上使程序更富有逻辑且易懂。因此,在程序中使用字符串是无法避免的,在本节中将介绍一些对字符串的基本操作,在后续章节中还会对字符串处理进行更详细的介绍。
在此,就从简单的字符串标识符号开始介绍,示例为“Hello, Optimus Prime!”(“你好,擎天柱!”),如图2-19所示。

图2-19 双引号和单引号标识字符串
在Python中既可以使用双引号也可以使用单引号来标识字符串,得到的结果是相同的。你可能会产生疑问:“设置一个符号来标识字符串不是更为简单吗?”答案如图2-20所示。

(a)双引号标识带单引号字符串

(b)单引号标识带双引号字符串
图2-20 单引号和双引号交替使用标识字符串
在图2-20(a)中,字符串中含有单引号。因此,使用单引号来标识整个字符串时Python IDLE会报错;然而,使用双引号来标识带有单引号的字符串时不会报错。接着看图2-20(b),当字符串中包含双引号时,可以使用单引号标识整个字符串。除此之外,还有一种方式是使用字符反斜线(\)对引号进行转义,处理方式如图2-21所示。

图2-21 使用反斜线字符转义单引号和双引号
下面给出一个更为复杂的字符串,其中既包含单引号也包含双引号,如图2-22所示。

图2-22 处理包含单引号和双引号的字符串
你会发现IDLE输出的字符串表达式中多出了一个反斜线,但这并没有任何影响。对这个字符串使用print函数就会看到字符串输出的结果,如图2-23所示。

图2-23 包含单引号和双引号的字符串输出结果
使用反斜线字符时还会遇到一个常见的问题,比如与字母n连用,请看图2-24所示的两个示例。

(a)输入换行符

(b)不输入换行符
图2-24 反斜线字符与字母n连用的处理
图2-24(a)中要将“Hello,”放在第一行,“Optimus Prime!”放在第二行,而图2-24(b)中要使用字符串表示文件路径,恰巧的是文件夹名称的首字母是“n”,它与反斜线组成了“\n”。Python使用“\n”代表换行符,当不是实际输入换行符且反斜线字符需要与字母n连用时,需要在反斜线前面再输入一个反斜线,因此在输出表示路径的字符串时使用’E:\\nest’。
接下来,另一个基本操作是拼接字符串,这在编程中经常遇到,比如在之前的场景中已经定义好了一个字符串,如果在后续的开发中需要在之前定义好的字符串基础上添加新字符串,就可以使用字符串的拼接。
如图2-25所示,直接将两个字符串以加号(+)串联,是通用且标准的字符串拼接方法。

图2-25 字符串拼接
当然你可能会发现在IDLE中直接输入
>>>"Hello, ""Sam!"
执行后也会得到正确的结果。但作者不推荐这样进行拼接,因为这并不规范。
回想一下前面学习过的反斜线字符,当字符串中含有较多“\n”时,如果不是实际输入换行符,可以使用原始字符串,它让字符串中字符全部保持原样。如图2-26所示,为字符串增加前缀字母r标识原始字符串。另外,当字符串中包含反斜线时,该字符串不能以反斜线结束,如果需要以反斜线结束就需要使用字符串拼接方法,如图2-26最后两条命令所示。

图2-26 使用原始字符串以及字符串拼接方法处理以反斜线字符结束的字符串
本节前面部分介绍的示例都是长度较短的字符串,对于长度较长的字符串应该如何标识呢?下面以电影《变形金刚1》中的开场白(对其中文释义感兴趣的读者可自行翻译)为例介绍如何处理长字符串。
如图2-27(a)和图2-27(c)所示,Python既可以使用3个单引号也可以使用3个双引号来标识长字符串,当字符串中文字无须换行时应使用反斜线对换行符进行转义,你可以通过对比图2-27(b)和图2-27(d)发现其奥妙所在。

(a)使用3个单引号标识长字符串

(b)长字符串中存在换行的结果

(c)使用3个双引号标识长字符串

(d)使用反斜线转义换行符
图2-27 处理长字符串
本节的最后将介绍几个处理字符串的基本函数和类,仍然以包含换行符“\n”的字符串为例,通过这种字符串你可以很清晰地看到str函数和repr函数的区别。
如图2-28所示,str函数会将特殊字符转换为相应的字符,其结果是编程者预计输出的字符串,而repr函数会保留字符串中的特殊字符不做处理。

图2-28 str函数与repr函数处理字符串的区别
len函数用于统计字符串的长度,其使用方法如图2-29所示。

图2-29 len函数的使用方法
图2-29中使用len函数分别计算图2-28中的字符串的长度,以及结合str函数和repr函数获取的字符串计算其长度。可见repr函数实际上就是对str函数中的字符串使用原始字符串处理方式,因此其结果总是比str函数多3个字符,分别是r、单引号或双引号、单引号或双引号。
字符串中一个有趣的地方是字符串的编码,Python使用Unicode来表示文本,对于Unicode字符以及名称,你可在其官方网站中找到更多详细的介绍。作者相信通过几个简单的示例,你就能明白如何在Python中处理Unicode编码。Unicode字符使用16位或32位的十六进制数(前缀为\u或\U)以及Unicode字符的名称\N{name}来表示。如图2-30所示,16位字符\u03A9代表希腊字母Ω,32位字符\U0001F42C代表海豚, 32位字符\U0001F47D代表外星人, \N{Eagle}代表老鹰。需要注意的是,32位字符需要在字符高位补零。

图2-30 Unicode字符及其表示的含义
Unicode是互联网上常见语言书写文本时使用较为普遍的编码。对于较旧的系统,仍然使用ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)编码,尤其是进行单字节编码时。为此Python提供两个类似的类:不可变bytes和可变bytearray。它们可指定字符串的编码类型,以使不同系统之间传输字符串不会出现乱码。图2-31中给出了使用bytes对不同字符串采用不同编码的处理结果。
在图2-31中,前面3条命令分别对字符串“Hello, Sam!”使用ASCII、UTF-8以及UTF-32进行编码。第四条命令中字符串是作者通过翻译软件将“Hello, Sam!”翻译为希腊语的结果,可见通过ASCII编码Python IDLE会报错。第五条命令通过UTF-8编码后可获得字符串。第六条命令使用bytes对象的decode方法,将第五条命令中使用指定编码类型得到的编码结果译码回原字符串。如果你仍然坚持使用ASCII编码,Python的处理方式是通过向encode方法传入相应参数告诉编译器如何处理错误,图2-32所示的几种方法均可产生编码结果。

图2-31 对字符串进行编码

图2-32 通过encode处理ASCII编码错误
读者一般无须担心处理字符串的编码问题,因为Python默认使用UTF-8编码,如果没有特殊需要,无须指定编码类型。最后,介绍bytearray类。如前所述,bytes类不允许修改字符串,而bytearray刚好相反,示例如图2-33所示。

(a)使用bytearray修改字符串中字符

(b)bytearray中声明编码类型
图2-33 bytearray使用方法
图2-33(a)中的字符b前缀代表对字符串创建bytes类,然后通过bytearray修改类属性,随后必须使用ord函数在获得其序号值后修改相应字符,修改后的结果图2-33(a)中也有所显示。如果首先创建bytearray类,则需要在bytearray中指定编码类型,否则会出错,如图2-33(b)所示。
至此,对于字符串的介绍暂告一段落,在后续章节中还会继续深入地介绍更多对字符串的操作方法。