>>> from env_helper import info; info()
页面更新时间: 2024-03-29 09:21:35
运行环境:
    Linux发行版本: Debian GNU/Linux 12 (bookworm)
    操作系统内核: Linux-6.1.0-18-amd64-x86_64-with-glibc2.36
    Python版本: 3.11.2

1.4. 正则表达式字符详解

1.4.1. 字符分类

在前面电话号码正则表达式的例子中, 你知道 \d 可以代表任何数字。 也就是说, \d 是正则表达式(0|1|2|3|4|5|6|7|8|9)的缩写。 有许多这样的“缩写字符分类”,如表7-1所示。

常用字符分类的缩写代码

缩写字符分类 | 表示

  • \d | 0到9的任何数字

  • \D | 除0到9的数字以外的任何字符

  • \w | 任何字母,数字或下划线字符,可以认为是匹配,单词,字符,

  • \W | 除字母,数字和下划线以外的任何字符

  • \s | 空格,制表符或换行符,可以认为是匹配,空白 ,字符

  • \S | 除空格,制表符和换行符以外的任何字符,

字符分类对于缩短正则表达式很有用。 字符分类[0-5]只匹配数字0到5, 这比输入(0|1|2|3|4|5)要短很多。

例如,在交互式环境中输入以下代码:

>>> import re
>>> xmasRegex = re.compile(r'\d+\s\w+')
>>> xmasRegex.findall('12 drummers, 11 pipers, 10 lords, 9 ladies, 8 maids, 7 swans, 6 geese, 5 rings, 4 birds, 3 hens, 2 doves, 1 partridge')
['12 drummers',
 '11 pipers',
 '10 lords',
 '9 ladies',
 '8 maids',
 '7 swans',
 '6 geese',
 '5 rings',
 '4 birds',
 '3 hens',
 '2 doves',
 '1 partridge']

正则表达式 \d+\s\w+ 匹配的文本有 一个或多个数字 (\d+) ,接下来是一个空白字符(\s) , 接下来是一个或多个字母/数字/下划线字符(\w+)findall()方法将返回所有匹配该正则表达式的字符串, 放在一个列表中。

1.4.2. 建立自己的字符分类

有时候你想匹配一组字符,但缩写的字符分类 ( \d\w\s 等)太宽泛。 你可以用方括号定义自己的字符分类。例如, 字符分类[aeiouAEIOU]将匹配所有元音字符, 不论大小写。在交互式环境中输入以下代码:

>>> import re
>>> vowelRegex = re.compile(r'[aeiouAEIOU]')
>>> vowelRegex.findall('RoboCop eats baby food. BABY FOOD.')
['o', 'o', 'o', 'e', 'a', 'a', 'o', 'o', 'A', 'O', 'O']

也可以使用短横表示字母或数字的范围。例如, 字符分类[a-zA-ZO-9]将匹配所有小写字母、 大写字母和数字。

请注意,在方括号内,普通的正则表达式符号不会被解释。 这意味着,你不需要前面加上倒斜杠转义.*?()字符。例如, 字符分类将匹配数字0到5和一个 句点。你不需要将它写成[0-5\.]

通过在字符分类的左方括号后加上一个插入字符(^), 就可以得到“非字符类”。 非字符类将匹配不在这个字符类中的所有字符。 例如,在交互式环境中输入以下代码:

>>> import re
>>> vowelRegex = re.compile(r'[^aeiouAEIOU]')
>>> print(vowelRegex.findall('RoboCop eats baby food. BABY FOOD.'))
['R', 'b', 'C', 'p', ' ', 't', 's', ' ', 'b', 'b', 'y', ' ', 'f', 'd', '.', ' ', 'B', 'B', 'Y', ' ', 'F', 'D', '.']

1.4.3. 插入字符和美元字符

可以在正则表达式的开始处使用插入符号(^),表明匹配必须发生在被查找文本开始处。类似地,可以再正则表达式的末尾加上美元符号($),表示该字符串必须以这个正则表达式的模式结束。可以同时使用 ^$,表明整个字符串必须匹配该模式,也就是说,只匹配该字符串的某个子集是不够的。

例如,正则表达式 r'^Hello' 匹配以 'Hello' 开始的字符串。 在交互式环境中输入以下代码:

>>> import re
>>> beginsWithHello = re.compile(r'^Hello')
>>> beginsWithHello.search('Hello world!')
<re.Match object; span=(0, 5), match='Hello'>
>>> beginsWithHello.search('He said hello.') == None
True

正则表达式 r'\d$' 匹配以数字0到9结束的字符串。 在交互式环境中输入以下代码:

>>> endsWithNumber = re.compile(r'\d$')
>>> endsWithNumber.search('Your number is 42')
<re.Match object; span=(16, 17), match='2'>
>>> endsWithNumber.search('Your number is forty two.') == None
True

正则表达式 r'^\d+$' 匹配从开始到结束都是数字的字符串。 在交互式环境中输入以下代码:

>>> wholeStringlsNum = re.compile(r'^\d+$')
>>> wholeStringlsNum.search('1234567890')
<re.Match object; span=(0, 10), match='1234567890'>
>>> wholeStringlsNum.search('12345xyz67890') == None
True
>>> wholeStringlsNum.search('12 34567890') == None
True

前面交互式脚本例子中的最后两次 search() 调用表明, 如果使用了 a$, 那么整个字符串必须匹配该正则表达式。

我总是会混淆这两个符号的含义, 所以我使用助记法 “Carrots cost dollars”, 提醒我插入符号在前面,美元符号在后面。

1.4.4. 通配字符

在正则表达式中,. (句点)字符称为“通配符”。 它匹配除了换行之外的所有字符。 例如,在交互式环境中输入以下代码:

>>> import re
>>> atRegex = re.compile(r'.at')
>>> atRegex.findall('The cat in the hat sat on the flat mat.')
['cat', 'hat', 'sat', 'lat', 'mat']

要记住,句点字符只匹配一个字符,这就是为什么在前面的例子中, 对于文本 flat ,只匹配 lat 。要匹配真正的句点, 就是用倒斜杠转义:\.

1.4.5. 用点-星匹配所有字符

有时候想要匹配所有字符串。例如, 假定想要匹配字符串 'FirstName:' , 接下来是任意文本,接下来是 'LastName:', 然后又是任意文本。可以用点-星(.*)表不“任意文本”。 回忆一下,句点字符表示“除换行外所有单个字符”, 星号字符表示“前面字符出现零次或多次”。

在交互式环境中输入以下代码:

>>> nameRegex = re.compile(r'First Name: (.*) Last Name: (.*)')
>>> mo = nameRegex.search('First Name: A1 Last Name: Sweigart')
>>> mo.group(1)
'A1'
>>> mo.group(2)
'Sweigart'

点-星使用“贪心”模式:它总是匹配尽可能多的文本。 要用“非贪心”模式匹配所有文本,就使用点-星和问号。 像和大括号一起使用时那样,问号告诉Python用非贪心模式匹配。 在交互式环境中输入以下代码, 看看贪心模式和非贪心模式的区别:

>>> nongreedyRegex = re.compile(r'<.*?>')
>>> mo = nongreedyRegex.search('<To serve man> for dinner.>')
>>> mo.group()
'<To serve man>'
>>> greedyRegex = re.compile(r'<.*>')
>>> mo = greedyRegex.search('<To serve man> for dinner.>')
>>> mo.group()
'<To serve man> for dinner.>'

两个正则表达式都可以翻译成“匹配一个左尖括号,接下来是任意字符,接下来是一个右尖括号”。但是字符串 '<To serve man> for dinner.>'对右肩括号有两种可能的匹配。在非贪心的正则表达式中,Python匹配最短可能的字符串:'<To serve man>'。在贪心版本中,Python匹配最长可能的字符串:'<To serve man> for dinner>'

1.4.6. 用句点字符匹配换行

点-星将匹配除换行外的所有字符。 通过传入 re.DOTALL 作为 re.compile() 的 第二个参数,可以让句点字符匹配所有字符,包括换行字符。

在交互式环境中输入以下代码:

>>> noNewlineRegex =re.compile('.*')
>>> noNewlineRegex.search('Servethe public trust.\nProtect the innocent.\nUphold the law.').group()
'Servethe public trust.'
>>> newlineRegex =re.compile('.*', re.DOTALL)
>>> newlineRegex.search('Serve thepublic trust.\nProtect the innocent.\nUphold the law.').group()
'Serve thepublic trust.nProtect the innocent.nUphold the law.'

正则表达式 noNewlineRegex 在创建时没有向 re.compile() 传入 re.DOTALL,它将匹配所有字符,直到第一个换行字符。但是,newlineRegex 在创建时向 re.compile()传入了 re.DOTALL ,它将匹配所有字符。这就是为什么 newlineRegex.search()调用匹配完整的字符串,包括其中的换行字符。