3.5 字符串

本节将介绍Python中字符串和正则表达式的一些重要概念。字符串是计算机科学的基础,字符串的处理是实际应用中常见的任务[2]。Python支持处理字符串表达式的操作,例如,索引(通过偏移获取)、分片(抽取一部分)、合并(组合字符串)等。Python提供了许多功能强大的函数,用于解决常见程序设计任务,本节将详细介绍字符串模块中函数的使用。Python还拥有用于执行如模式匹配这样的高级文本处理的任务模块,用来查找具有复杂规则的字符串,简化字符串的处理程序。最后将介绍字符串函数和正则表达式的应用场合。

因为Python中所有的数据都是对象,所以有必要早点引进对象,这样就可以开始用它们来开发有用的程序。Python内置了强大的对象类型(或者叫作数据结构)作为语言的核心部分,让编程变得更简单和高效。本节只是简单地介绍了对象和字符串;本书将在第8章里进一步介绍对象。

3.5.1 字符和字符串

字符串(String)是Python处理的最为常见的数据类型之一。在日常生活中,所接触到典型的字符串有字母、单词、短语、句子、姓名、住址、门牌号等。字符串出现在几乎所有Python程序中,主要用途是存储和表现基于文本的信息。

Python没有字符数据类型,一个字符的字符串代表一个字符,空字符串用一对引号(单引号或双引号)括起来,其中什么都没有。Python处理字符和字符串的方式是一样的。

1.ASCII码

计算机内部使用二进制数。一个字符在计算机中是以0和1构成的序列形式来存储的。将字符映射成它对应的二进制形式的过程称为字符编码。字符有很多种编码的方式,编码表定义该如何编码每个字符。

大多数计算机采用ASCII码(美国信息交换标准代码),它是表示所有大小写字母、数字、标点符号和控制字符的8位编码表。ASCII码用0~127来编码这些字符。附录A中给出了ASCII码表示的字符。

2.统一码

Python支持Unicode码(统一码)。统一码是一种编码表,它能表示国际字符。统一码是由Unicode协会建立的一种编码方案,它支持使用世界各种语言所书写的文本的交换、处理和显示。统一码最初被设计为16位的字符编码。一个16位的编码所能产生的字符只有65536个,它是不足以表示全世界所有字符的。因此,Unicode标准被扩展为1112064个字符。这些字符都远远超过了原来16位的限制,它们称为补充字符(Supplementary Character)。对补充字符的处理和表示介绍超过了本书的范围,在此不做深入讨论。

一个统一码以“\u”开始,后面紧跟4个十六进制数字,它们从“\u0000”到“\uFFFF”。ASCII码是统一码的子集。Unicode码包括ASCII码,从“\u0000”到“\u007F”对应128个ASCII字符。

3.函数ord和chr

Python提供ord(ch)函数来返回字符ch的ASCII码,用chr(code)函数返回code所代表的字符,如下所示。

3.5.2 字符串字面值

1.创建字符串

和数字一样,字符串也是值,例如:

这个示例使用的是双引号,而Python打印字符串时,用单引号将其括起,这其实没有任何差别,如下所示。

这里使用的是单引号,结果完全相同,事实上,Python是同时支持单引号和双引号的。

还可以使用str()函数来构造字符串,如下所示。

上例中的"Hello,world!",这种由字符构成的一个整体,被称为字符串字面常量。在Python程序中,字符串字面常量可以表示为单引号或者双引号包围的一个字符序列。字符串中的字符可以是键盘上能够找到的任意字符(如英文字母、数字、标点符号和空格等)以及其他的特殊字符。

起始和结尾的引号必须是一致的(要么是两个双引号,要么是两个单引号)。当字符串使用双引号定义时,单引号可以直接出现在字符串中,但是双引号不可以。相似的,由单引号定义的字符串可以包含双引号,但是不能直接使用单引号。

在这里,字符串为'Let',因此Python不知道如何处理当前行余下的内容。可以使用反斜杠\对引号进行转义,如下所示。

这样Python将明白中间的引号是字符串的一部分,而不是字符串结束的标志。同样也可以用转义字符处理双引号。

像这样对引号进行转义很有用,而且在有些情况下必须这样做。例如,在字符串同时包含单引号和双引号(如'Let\'s say "Hello,world!"')时,如果不使用反斜杠进行转义,该如何处理?这种情况,Python为我们提供了原始字符串的表示方法。

提示 在本节后面还会详细介绍转义字符的相关内容。

2.长字符串和原始字符串

有一些独特而有用的字符串表示方式。例如,有一种独特的语法可用于表示包含换行符或反斜杠的字符串(长字符串和原始字符串)。

(1)长字符串

要表示很长的字符串(跨越多行的字符串),可使用三引号(而不是普通引号)。例如,下面的print语句内输出一个长字符串。

同样,还可使用三个双引号,如"""like this"""。请注意,三个引号让解释器能够识别表示字符串开始和结束的位置,因此字符串本身可包含单引号和双引号,无需使用反斜杠进行转义。

(2)原始字符串

原始字符串不以特殊方式处理反斜杠,因此在有些情况下很有用(编写正则表达式时,原始字符串很有用)。在常规字符串中,反斜杠扮演着特殊角色:它对字符进行转义,让你能够在字符串中包含原本无法包含的字符。例如,你已经看到可使用\n表示换行符,从而像下面这样在字符串中包含换行符。

在这样的情况下,原始字符串可派上用场,因为它们根本不会对反斜杠做特殊处理,而是让字符串包含的每个字符都保持原样。原始字符串除在字符串的第一个引号前加上字母“r”(可以大小写)以外,与普通字符串有着几乎完全相同的语法。

看起来可在原始字符串中包含任何字符,这大致是正确的。一个例外是,引号需要像通常那样进行转义,但这意味着用于执行转义的反斜杠也将包含在最终的字符串中。

但如果要指定以反斜杠结尾的原始字符串(如以反斜杠结尾的路径),基本技巧是将反斜杠单独作为一个字符串,下面是一个简单的示例。

3.5.3 索引和切片

本节将介绍另一个新的概念:数据结构。数据结构是以某种方式(如通过编号)组合起来的数据元素(如数、字符乃至其他数据结构)集合。在Python中,最基本的数据结构为序列(Sequence)。序列中的每个元素都有编号,及其位置或索引,其中第一个元素的索引为0,第二个元素的索引为1,以此类推,从0开始指出元素相对于序列开头的偏移量,而最后一个字符的索引是-1。

回到本节,像任意字符的集合一样,字符串是单个字符的字符串序列,其他类型的序列还包括列表和元组(稍后将介绍)。严格地说,Python字符串被划分为不可变序列这一类别,意味着这些字符串所包含字符存在从左到右的位置顺序,并且它们不可以在原处修改。实际上,字符串是我们将学习的从属于稍大一些的对象类别——序列的第一个代表。请留意本节所介绍的序列操作,因为它在后面要学习的其他序列类型(如列表和元组)中同样适用。

字符串即是字符序列,字符串中字符所在的位置或索引也是使用0、1、2、3…来标识的。字符序列中的所有元素都有编号(从0开始递增)。可以像下面这样使用编号来访问各个元素。

注意 字符串就是由字符组成的序列。索引0指向第一个元素,这里为字母H。不同于其他一些语言,Python没有专门用于表示字符的类型,因此一个字符就是只包含一个元素的字符串。

使用编号来访问元素称为索引(Indexing),它可以用来获取元素,这种索引方式适用于所有序列。当使用负数索引时,Python将从右(即从最后一个元素)开始往左数,因此-1是最后一个元素的位置。

对于字符串字面量(以及其他的序列字面量),可直接对其执行索引操作,无需先将其赋给变量。这与先赋给变量再对变量执行索引操作的效果是一样的。

除使用索引来访问单个元素外,还可使用切片(Slicing)来访问特定范围内的元素。为此,可使用两个索引,并用冒号分隔。

切片适用于提取序列的一部分,其中的编号非常重要:第一个索引是包含的第一个元素的编号,但第二个索引是切片后余下的第一个元素的编号,请看下面的示例。

简而言之,提供两个索引来指定切片的边界,其中第一个索引指定的元素包含在切片内,但第二个索引指定的元素不包含在切片内。代码清单3-7给出了一个索引和切片操作的示例。

代码清单3-7 索引和切片操作

运行结果:

3.5.4 反向索引

上文讨论的索引是按照字符串自左向右而确定的。如果要从列表末尾开始数,也可以使用负数索引。通过反向索引,最右端的字符索引值为-1,它左边的一个字符索引值为-2,以此类推。

然而,这样好像无法包含最后一个元素。如果使用索引0,即到达列表末尾后再前一步所处的位置,结果将如何呢?

结果并不是你想要的。事实上,执行切片操作时,如果第一个索引指定的元素位于第二个索引指定的元素后面(在这里,倒数第3个元素位于第1个元素后面),结果就为空序列。好在你能使用一种简写:如果切片结束于序列末尾,可省略第二个索引。

同样,如果切片始于序列开头,可省略第一个索引。

实际上,要复制整个序列,可将两个索引都省略。

程序清单3-8给出了一个字符串反向索引的示例。

代码清单3-8 反向索引示例

运行结果:

3.5.5 切片的默认边界

在表达式strName[m:n]中,其中一个或者两个边界都是可以忽略的。在这种情况下,左边边界m的默认值为0,右边边界的默认值是字符串的长度。也就是,strName[:n]包括了从字符串首字符到strName[n-1]之间的所有字符,strName[m:]包括从strName[m]到字符串末尾的所有字符。切片strName[:]正好表示整个字符串strName。而切片strName[m:n]可以理解为数学中的开闭区间[m,n)。用法可见代码清单3-9。

代码清单3-9 默认边界示例

运行结果:

3.5.6 索引和切片越界

Python中不允许序列中的单个元素的索引越界,程序中字符串序列越界,编译器会报IndexError的错误。例如:

但是在切片中可以允许索引越界。如果切片的左边索引过小,切片会从序列的第一项开始,如果切片的右边索引过大,切片会一直到序列的最后一项。

提示 在负数作为索引值时是从-1开始的,而不是从0开始的,即最后一个元素的下标为-1,这是为了避免和第一个元素产生冲突。

3.5.7 字符串拼接

两个字符串可以拼接起来组成一个新的字符串。可使用加法运算符“+”来拼接字符串。

由字符串、标点符号、函数和方法构成的一个可运算字符串称为字符串表达式。需要注意,字符串不允许直接与其他类型的数据拼接。下面的语句将会报TypeError的错误。

解决上述问题,可以用str()函数将整数转换成字符串,然后以拼接字符串的方法输出该内容,将print输出改为如下语句。

如果重复连接一个字符串可以使用星号操作符,将字符串与数x相乘时,将重复这个字符串x次来创建一个新的字符串。

3.5.8 常见字符串函数

字符串的函数有很多,其很多方法都是从模块string那里“继承”而来的。字符串函数操作将字符串作为输入并返回相应的值,使用方法的一般表达形式为:

字符串是一种常见的数据类型,我们经常会面临各式各样的字符串处理问题,那么,这就要求我们必须掌握一些常用的字符串处理函数。下面对Python中常用的字符串操作方法进行介绍。

1.搜索子字符串

在Python中,字符串对象提供了很多用于查找字符串的方法,这里主要介绍以下几种方法。

(1)find()方法

该方法用于检索是否包含指定的子字符串。如果包含特定字符串,则返回开始的索引;否则,返回-1。其语法格式如下。

使用方法如下。

·str:表示原始字符串。

·sub:表示待检索的子字符串。

·start:可选参数,表示检索范围的起始索引,若不指定,则从头查找。

·end:可选参数,表示检索范围的结束索引,若不指定,则检索到结尾停止。

用法见如下示例。

说明Python还提供了rfind()方法,其作用与find()方法类似,只是字符串从右边开始查找,返回字符串的最高下标。

(2)index()方法

index()方法和find()方法类似。也适用于检测字符串是否包含指定字符,如果包含,则返回开始的索引值;否则,抛出异常。其语法格式如下。

使用方法如下。

·str:表示原字符串。

·sub:表示待检索的子字符串。

·start:可选参数,表示检索范围的起始索引,若不指定,则从头查找。

·end:可选参数,表示检索范围的结束索引,若不指定,则检索到结尾停止。

用法见如下示例。

说明Python的字符串还提供了从右边开始查找的reindex()方法,返回这个字符串的最高下标。

(3)count()方法

该方法用于检索指定字符串在另一个字符串中出现的次数。如果返回值为0,则说明检索的字符串不存在。其语法格式如下。

使用方法如下。

·str:表示原字符串。

·sub:表示要检索的子字符串。

·start:可选参数,表示检索范围的起始索引,若不指定,则从头查找。

·end:可选参数,表示检索范围的结束索引,若不指定,则检索到结尾停止。

用法见如下示例。

(4)startwith()方法

该方法用于检查字符串是否以指定字符串开头。若是,则返回True;否则,返回False。其语法格式如下。

使用方法如下。

·str:表示原字符串。

·prefix:表示待检索的子字符串前缀。

·start:可选参数,表示检索范围的起始索引,若不指定,则从头查找。

·end:可选参数,表示检索范围的结束索引,若不指定,则检索到结尾停止。

用法见如下示例。

(5)endwith()方法

该方法用于检查字符串是否以字符串结尾。若是,则返回True;否则,返回False。其语法格式如下。

使用方法如下。

·str:表示原字符串。

·suffix:表示待检索的子字符串前缀。

·start:可选参数,表示检索范围的起始索引,若不指定,则从头查找。

·end:可选参数,表示检索范围的结束索引,若不指定,则检索到结尾停止。

用法见如下示例。

2.转换字符串

(1)lower()方法

lower()方法用于将字符串中的所有字母转换为小写。lower()方法的语法格式如下。

其中,str为要进行转换的字符串,示例如下。

(2)upper()方法

upper()方法将字符串的所有字母转换为大写。upper()方法的语法格式如下。

其中,str为要进行转换的字符串,使用方法如下。

(3)capitalize()方法

capitalize()方法将字符串的首字母大写,其余字母全部小写。capitalize()方法的语法格式如下。

其中,str为要进行转换的字符串,用法如下。

(4)title()方法

title()方法将字符串中的所有单词的首字母大写,其余字母全部小写。值得注意的是,这里单词的区分是以任何标点符号区分的,即标点符号的前后都是一个独立的单词,字符串最后一个标点除外。该方法的语法格式如下。

其中,str为要进行转换的字符串。

(5)replace()方法

replace()方法返回一个新的字符串,它用一个新字符串替换旧字符串所有出现的地方,其语法格式如下。

参数说明如下。

·str:表示原字符串。

·old:将要被替换的旧字符串。

·new:新字符串,用来替换旧的字符串(替换一次或者多次old)。

·max:用来替换的次数,这里有两种:1)当不将max参数传入时,默认将所有old字符或者字符串替换为new字符或者字符串;2)当我们将max参数传入后,则将旧字符串替换为新字符串不超过max次,多余的则不进行替换。

用法见如下示例。

3.分割、合并字符串

分割字符串是把字符串分割为列表,而合并字符串是把列表合并为字符串,这两个操作可以看作是互逆的动作。

(1)分割字符串

split()方法可以实现字符串分割,把字符串按照指定的分割符切分为字符串列表。split()方法的语法格式如下。

使用方法如下。

·str:表示要进行分割的字符串。

·sep:用于指明分割符,可以包含多个字符,默认为None,即所有空字符(包括空格、换行、“\n”、制表符“\t”等)。

·maxsplit:可选参数,用于指定分割的次数,如果不指定或者为-1,则分次数没有限制,返回结果列表的元素个数,个数最多为maxsplit+1。

(2)合并字符串

join()方法可以实现合并字符串,它利用固定的分隔符将多个字符串连接在一起,其语法格式如下。

使用方法如下。

·newStr:表示合并后生成的新字符串。

·oldStr:字符串类型,用于指定合并时的分割符。

·sequence:要连接的元素序列、字符串、元组、字典。

比如想让字符串去掉引号,且空一格输出可以采取如下方法。

4.删除字符串中的空格

在一些情况下,字符串需要去除空格和特殊字符。

(1)strip()方法

该方法用于去掉字符串左、右两侧的空格和特殊字符,即开头和结果的所有chars字符都删除,语法格式如下。

使用方法如下。

·str:为要去除空格的字符串。

·chars:可选参数,用于指定要去除的字符,可以指定多个。如果不指定chars参数,默认去除空格、制表符、回车符、换行符等空白字符。

(2)lstrip()方法

该方法用于去掉字符串左侧的空格和特殊字符串,语法格式如下。

使用方法如下。

·str:为要去除空格的字符串。

·chars:可选参数,用于指定要去除的字符,可以指定多个。如果不指定chars参数,默认去除空格、制表符、回车符、换行符等空白字符。

(3)rstrip()方法

该方法用于去掉字符串右侧的空格和特殊字符,语法格式如下。

使用方法如下。

·str:为要去除空格的字符串。

·chars:可选参数,用于指定要去除的字符,可以指定多个。如果不指定chars参数,默认去除空格、制表符、回车符、换行符等空白字符。

提示 用strip()方法来删除字符串末尾不需要的字符,这是很好的经验。

5.其他一些方法

除了前面介绍的一些方法外,还有一些常用的(如计算字符串长度和测试字符串)方法。

(1)计算字符串长度

Python提供了len()方法计算字符串的长度,语法格式如下。

如果想打印字符串奇数位置的字符可以使用如下代码。

len()方法返回一个字符串中的字符个数,而max()和min()方法返回字符串中的最大和最小字符。

(2)测试字符串

字符串还有很多有用的方法。有一些可以测试字符串中的字符的方法。

isalnum()方法检查字符串是否都是字母或是数字,如果这个字符串中的字符是字母或数字且至少有一个字符,则返回True。

isalpha()方法检查字符串是否都是字母,如果这个字符串中的字符是字母且至少有一个字符,则返回True。

isdigit()方法检查字符串是否都是数字,如果这个字符串中的字符只含有数字字符,则返回True。

isidentifier()方法检查字符串是否可用作Python标识符,如果这个字符串中的字符是Python标识符,则返回True。

islower()方法检查字符串中的所有字母是否都是小写的,如果这个字符串中的所有字符全是小写的且至少有一个字符,则返回True。

isupper()方法检查字符串中的所有字母是否都是大写的,如果这个字符串中的所有字符全是大写的且至少有一个字符,则返回True。

isspace()方法检查字符串中的字符是否都是空白字符,如果这个字符串只包含空格,则返回True。

下面是一些使用字符串测试方法的例子。

为了帮助大家入门,本书总结并展示了一些最常用方法的代码。表3-5概括了Python3.0中内置字符串对象的方法及对应的使用模式。

表3-5 Python3.0中的字符串方法调用

3.5.9 格式化数字和字符串

1.格式化数字

我们常常希望显示某种格式的数字。例如,下面是计算利息的代码。

由于利息是货币数字,我们只希望显示小数点后两位数字。为达到要求,我们将print()方法做出如下改动。

但是我们希望输出结果为15.60,而不是15.6,可以使用format()方法来修改。将print()方法再做出如下修改就能获得我们期望的格式。

format()方法的语法格式如下。

其中item是数字或者字符串,用说明符(Format-Specifier)指定条目item的格式。

(1)格式化浮点数

如图3-4所示,如果条目是一个浮点值,可以用“width.precision f”的形式给出格式的宽度和精确度。宽度width是指得到的字符串的宽度,精确度precision指定小数点后数字的个数,f被称为转换码,它为浮点数设定格式。例:

图3-4 格式化浮点数

结果显示如下。

format("10.2f")方法将数字格式化成宽度为10,包括小数点后两位小数的字符串。这个数字被四舍五入到两个小数位。这样,在小数点前面分配7个数字。如果在小数点前的数字小于7个,则在数字前插入空格。如果小数点前的数字大于7个,则数字的宽度将会自动增加。

如果省略宽度符,它就默认为0,宽度会自动根据格式化这个数所需的宽度自动设置。

结果显示如下。

(2)格式化整数

“d”“x”“o”“b”转换码分别用来格式化十进制整数、十六进制整数、八进制整数和二进制整数,可以指定转换的宽度。

在默认情况下,一个数的格式是向右对齐的。可以将符号“<”放在说明符里指定得到的字符串是以指定的宽度向左对齐的。

(3)格式化成科学记数法

如果将转换码变成e,数字将被格式化为科学计数法。

符号“+”和“-”被算在宽度里。(4)格式化成百分数

可以使用转换码“%”将一个数字格式化成百分数。

符号“%”也被算在宽度里。

2.格式化字符串

格式化字符串是指先制定一个模板,在这个模板中预留几个位置,然后根据需要填上相应的内容。这些需要通过指定的符号标记(也叫占位符),而这些符号还不会显示出来。Python中,有两种方法格式化字符串。

(1)使用“%”操作符

在Python中,要实现格式化字符串,可以使用“%”操作符,语法格式如下。

使用方法如下。

·-:可选参数,用于指定左对齐,正数前方无符号,负数前方加负号。

·+:可选参数,用于指定右对齐,正数前方无符号,负数前方加负号。

·0:可选参数,表示右对齐,正数前方无符号,负数前方加负号,用0填充空白处(一般与m参数一起使用)。

·m:可选参数表示占有宽度。

·n:可选参数,表示小数点后保留的位数。

·格式化字符串:用于指定类型。详情见表3-6。

·exp:要转换的项。如果要指定多个项,需要通过元组进行指定,但不能通过列表。

表3-6 常用格式化字符

上述格式字符串中的“%s”称为转换说明符,指出了要将值插入什么地方。另外,请记住格式化总是返回新的字符串作为结果而不是对左侧的字符串进行修改;由于字符串是不可变的,所以只能这样操作。如果需要的话,可以分配一个变量名来保存结果。

说明 由于使用%操作符是早期Python中提供的方法,自从Python2.6版本开始,字符串提供了format()方法对字符串进行格式化。Python社区现在推荐使用这种方法。

(2)使用format()方法

字符串对象提供了format()方法用于进行字符串格式化,语法格式如下。

使用方法如下。

·format:用于指定字符串的显示样式(即模板)。

·args:用于指定要转换的项,如果有多项,则用逗号进行分隔。

下面重点介绍创建模版。在创建模版时,需要使用“{}”和“:”指定占位符,语法格式如下。

使用方法如下。

·index:可选参数,用于指定要设置格式的对象在参数列表中的索引位置,索引值从0开始。如果省略,则根据值的先后顺序自动分配。

·fill:可选参数,用于指定空白处填充的字符。

·align:可选参数,用于指定对齐方式(值为“<”时表示内容左对齐;值为“>”时表示内容右对齐;值为“=”时表示内容右对齐,将符号放在填充内容的最左侧,且只对数字生效;值为“^”时表示内容居中),需要配合width一起使用。

·sign:可选参数,用于指定有无符号数字(值为“+”表示整数加正号,负数加负号;值为“-”表示正数不变,负数加负号,值为空格表示正数加空格,负数加负号)。

·#:可选参数,对于二进制、八进制、和十六进制,如果加上#号,表示会显示0b/0o/0x前缀,否则不显示。

·width:可选参数,用于指定所占宽度。

·precision:可选参数,用于指定保留的小数位数。

·type:可选参数,用于指定类型。

format()方法中常用的格式化字符见表3-7。

表3-7 format()方法中常用的格式化字符

下面是一些示例。

字符串格式设置涉及的内容很多,因此即便是这里的完整版也无法全面探索所有的细节,而只是介绍主要的组成部分。这里的基本思想是对字符串调用方法format,并提供要设置其格式的值。

3.5.10 正则表达式

我们经常需要编写代码来验证用户输入,比如检测输入是否是一个数字,或者是否是一个全部小写字母的字符串,或者是否是一个社会安全号。如何编写这类代码呢?一个简单有效完成该任务的方法是使用正则表达式。

正则表达式(Regular Expression)(缩写regex)是一个字符串,用于描述匹配一个字符串集的模式[3]。可以通过指定某个模式来匹配、替换或分隔一个字符串。这是一种非常有用且功能强大的特性。

所有的现代编程语言都有内建字符串处理函数。在Python里查找、替换字符串的方法是index()、find()、split()、count()、replace()等。但这些方法都只是最简单的字符串处理。比如,用index()方法查找单个子字符串,而且查找总是区分大小写的。为了使用不区分大小写的查找,可以使用s.lower()或者s.upper(),但要确认你查找的字符串的大小写是匹配的。replace()和split()方法有相同的限制。

如果使用string的方法就可以达到你的目的,那么你就使用它们,因为它们速度快又简单,并且很容易阅读。但是如果你发现自己要使用大量的if语句,以及很多字符串函数来处理一些特例,或者说你需要组合调用split()和join()来切片、合并你的字符串,你就应该使用正则表达式。

1.行定位符

行定位符就是用来描述字符串的边界,“^”表示行的开始,“$”表示行的结尾。如:

该表达式表示要匹配的字符串td的开始位置是行头,如可以匹配“td means today”,但是不可以匹配“today shorts for td”。

但如果使用“td&”,则可以匹配后者。

2.元字符

正则表达式里有很多元字符,这些有特殊字符是构成正则表达式的要素,见表3-8。

表3-8 常用元字符

比如:

匹配以字母te开头的单词,先是从某个单词开始处(\b),然后匹配字母te,接着是任意数量字母或数字(\w*),最后是单词结束处(\b)。该表达式可以匹配“tenserflow”“tense123”等。

3.限定符

如果要匹配电话号码,需要形式如“\d\d\d\d-\d\d\d\d\d\d\d”这样的正则表达式。其中出现了11次“\d”,表达方式烦琐。而且有些地区的电话号码是8位数字,区号也有可能是3位或者4位数字,因此这个正则表达式就失去了作用。但是提供了如“*”这样的符号可以对某个部分多次匹配,这些字符被称为限定符。

常见的限定符见表3-9。

表3-9 常用限定符

借助限定符可以利用下面的形式表示电话号码。

4.字符类

正则表达式查找数字和字母是很简单的,因为已经有了对应这些字符集合的元字符,但是如果要匹配没有预定义元字符的字符集合,只需要在方括号中列出它们就行了。表3-10总结了特殊字符的表示方法。

表3-10 特殊字符表示方法

比如,[.?!]匹配标点符号(“.”“?”“!”);[0-9]匹配0~9的数字和“\d”的含义是一样的。

5.排除字符

如果想要匹配不符合指定字符串集合的字符串,可以利用正则表达式提供的“^”字符。放在方括号中不再表示行开始的意思,而是表示排除的意思。例如:

表示不是一个字母的字符。

6.选择字符

如果要选择匹配多个条件,就要使用选择字符“|”来实现。该字符可以理解为“或”,匹配身份证时身份证长度为15位或者18位。如果为15位,则全为数字;如果为18位,前17位为数字,最后一位为校验位,可能为数字也可能为X。匹配身份证的表达式也可以写成如下方式。

该表达式的意思是可以匹配15位数字,或者18位数字,或者17位数字和最后一位。最后一位可以是数字,也可以是X或者x。

7.转义字符

正则表达式中的转义字符“\”和Python中的没有区别,作用都是将特殊的字符变成普通的字符。例如,在正则表达式中“.”表示匹配任意的字符,在使用“.”时需要使用转义字符“\”。所以IP地址的正则表达式就可以用如下方法表示。

该表达式可以匹配比如“192.168.1.1”格式的IP地址。

8.分组

正则表达式中使用小括号分组,改变限定符的作用范围。比如上面的IP地址就可以改写成:

加上括号之后就变成了对([1-9]{1,3}\.)重复操作,也即括号里是一个子表达式。

9.在Python中使用正则表达式语法

在Python中使用正则表达式时,是将其作为模式字符串使用的。例如将匹配不是字母的一个字符串的正则表达式表示为模式字符串,可以使用下面的方法。

而如果字符串中包含有转义字符需要将其转义,这样的话会可能包含大量的特殊字符和反斜杠,所以需要使用原生字符串,在模式字符串前面加r或者R。

比如:

转义后的结果为:

而使用原生字符串后表达为:

为了编写方便,推荐使用正则表达式使用原生字符串。

3.5.11 使用re模块实现正则表达式

Python中的re模块支持正则表达式的匹配功能。re提供了一些根据正则表达式进行查找、替换、分隔字符串的函数。表3-11描述了其中一些重要的函数。

re模块在使用的时候需要先用import语句引入,语法格式如下。

表3-11 模块re中一些重要的函数

这些函数使用一个正则表达式作为第一个参数,re模块的一些函数都有一个flags参数,该参数用于设置匹配的附加选项,如是否忽略大小写、是否支持多行匹配等。表3-12列出了re模块的规则选项。

表3-12 re模块的规则选项

compile()方法将用字符串表示的正则表达式转换为模式对象,以提高匹配效率。调用search、match等方法时,如果提供的是用字符串表示的正则表达式,都必须在内部将它们转换为模式对象。通过使用函数compile对正则表达式进行转换后,每次使用它时都无需再进行转换。

这样就可以将那些经常使用的正则表达式编译成正则表达式对象,可以提高一定的效率。如:一句话包含5个英文单词,长度不一定,用空格分割,请把5个单词匹配出来。

模式对象也有搜索/匹配方法,因此re.search(pat,string)(其中pat是一个使用字符串表示的正则表达式)等价于pat.search(string)(其中pat是使用compile创建的模式对象)。编译后的正则表达式对象也可用于模块re中的普通函数中。

1.匹配字符串

匹配字符串可以使用re模块中提供的match()、search()和findall()方法。

(1)使用match()方法进行匹配

match()方法用于从字符串的开始处匹配,如果在起始位置匹配成功,则返回Match对象。否则返回None。其语法格式如下。

使用方法如下。

·pattern:表示匹配的正则表达式。

·string:要匹配的字符串。

·flags:标志位,用于控制正则表达式的匹配方式。如是否区分大小写、是否多行匹配等。

例如,匹配字符串是否以“bupt_”开头,不区分大小写字母,如代码清单3-10所示。

代码清单3-10 匹配字符串示例1

运行结果:

从上面的执行结果来看,字符串“BUPT_CS”以“bupt_”开头,将返回一个Match对象,而字符串“课程名称BUPT_CS bupt_cs”没有以“bupt_”开头,将返回None。这是因为match()方法从字符串的开始位置开始匹配,当第一个字母不符合条件时,则不再进行匹配,直接返回None。

在模块re中,查找与模式匹配的子串的函数都在找到时返回MatchObject对象。这种对象包含与模式匹配的子串的信息,还包含模式的那部分与子串的那部分匹配的信息。这些子串部分被称为编组。

编组就是放在圆括号内的子模式,它们是根据左边的括号数编号的,其中编组0指的是整个模式。因此,在下面的模式中:

包含下面的编组:

通常,编组包含诸如通配符和重复运算符等特殊字符,因此你可能想知道与给定编组匹配的内容。表3-13概括了re匹配对象的一些重要方法。

表3-13 描述了re匹配对象的一些重要方法

group()返回与模式中给定编组匹配的子串。如果没有指定编组号,则默认为0。如果只指定了一个编组号(或使用默认值0),将只返回一个字符串;否则返回一个元组,其中包含与给定编组匹配的子串。

start()方法返回与给定编组(默认为0,即整个模式)匹配的子串的起始索引。

end()方法类似于start,但返回终止索引加1。

span()方法返回一个元组,其中包含与给定编组(默认为0,即整个模式)匹配的子串的起始索引和终止索引。

代码清单3-11说明了方法的工作原理。

代码清单3-11 匹配字符串示例2

运行结果:

(2)使用search()方法进行匹配

search()方法在给定字符串中查找一个预指定正则表达式匹配的子串。如果找到这样的子串,将返回MathObject(结果为真),否则返回None(结果为假)。search()方法的语法格式如下。

使用方法如下。

·pattern:表示匹配的正则表达式。

·string:要匹配的字符串。

·flags:标志位,用于控制正则表达式的匹配方式。如是否区分大小写、是否多行匹配等。

代码清单3-12给出了搜索字符串的示例。

代码清单3-12 搜索字符串示例

运行结果:

从上面的运行结果可以看到,search()方法不仅是在字符串的起始位置搜索,其他位置有符合的匹配也可以进行搜索。

鉴于回值的这种特征,可以在条件语句中使用这个函数,如下所示。

(3)使用findall()方法进行匹配

find()方法用于整个字符串中搜索所有符合正则表达式的字符串,并以列表的形式返回。如果匹配成功,则返回包含匹配结构的列表,否则返回空列表。findall()语法的格式如下。

使用方法如下。

·pattern:表示匹配的正则表达式。

·string:要匹配的字符串。

·flags:标志位,用于控制正则表达式的匹配方式。如是否区分大小写、是否多行匹配等。

代码清单3-13 全局搜索子字符串

运行结果:

从代码清单3-13中可以看出,findall()方法返回一个列表,其中包含所有给定模式匹配的子串。例如要找出字符串包含的所有单词,可以进行如下操作。

要查找所有的标点符号,可以进行如下操作。

2.替换字符串

前面介绍了replaze()方法实现字符串的替换,同时可以使用re模块提供的sub()方法用于实现字符串替换,语法格式如下。

使用方法如下。

·pattern:表示匹配的正则表达式。

·repel:表示替换的字符串。

·string:表示要被查找替换的原始字符串。

·count:可选参数,表示模式匹配后替换的最大次数,默认值为0,表示替换所有的匹配。

·flags:可选参数,表示标志位,用于控制匹配方式,如是否区分大小写。

代码清单3-14 替换子字符串

运行结果:

在代码清单3-14中,第5行代码在分片s[-5:]范围内替换“Hello”,即在字符串“orld!”中替换“Hello”。由于没有找到匹配的子串,所以sub()返回s[-4:]。输出结果为“orld!”。第6行代码在分片s[-6:]范围内替换“World”,即把字符串“World!”替换为“China!”。

sub()方法先创建变量s的复制,然后在复制中替换字符串,并不会改变变量s的内容。

例如,下面的例子,将代码后的注释信息去掉。

下面的代码将手机号后4位替换成0。

3.分割字符串

split()方法用于实现根据正则表达式分割字符串,并以列表形式返回。其作用同字符串对象的split()方法类似,所不同的是分割字符串由模式字符串指定。split()方法的语法格式如下。

使用方法如下。

·pattern:表示匹配的正则表达式。

·repel:表示替换的字符串。

·string:表示要被查找替换的原始字符串。

·maxsplit:可选参数,表示最大的拆分次数。

·flags:可选参数,表示标志位,用于控制匹配方式,如是否区分大小写。

例如,使用字符串方法split()时,可以以字符串“,”为分隔符来分割字符串,但使用re.split()时,可以以逗号和空格作为分隔符来分割字符串。

这个例子可以看出,返回值为子串列表。参数maxsplit指定最多分割多少次。