参考:
re --- 正则表达式操作 — Python 3.12.0 文档
正则表达式指南 — Python 3.12.0 文档
正则表达式 – 教程 | 菜鸟教程 (runoob.com)
正则表达式——7种免费测试工具_正则表达式测试工具-CSDN博客
主要是指:由 re.compile() 返回的已编译正则表达式对象。
>>> import re
>>> pattern = re.compile("d")
>>> pattern.search("dog") # Match at index 0
>>> pattern.search("dog", 1) # No match; search doesn't include the "d"
search()和match()的区别:match()只在字符串的开头位置检测匹配,search()在字符串中的任何位置检测匹配(这也是 Perl 在默认情况下所做的)。例如:
>>> pattern = re.compile("o")
>>> pattern.search("dog")
>>> pattern.match("dog")
说明:
注意 MULTILINE 多行模式中函数 match() 只匹配字符串的开始,但使用 search() 和以 '^' 开始的正则表达式会匹配每行的开始
>>> re.match("X", "A\nB\nX", re.MULTILINE) # No match
>>> re.search("^X", "A\nB\nX", re.MULTILINE) # Match
Python标准库 - re -- 正则表达式 (1)中讲到的函数(re.split(), re.findall(), re.sub()....),都有对应的方法存在于正则表达式对象中。用法类似,只是参数中少了正则表达式对应的字符串(参数pattern),因为这个正则表达式已经在调用re.compile()的时候存在于这个正则表达式对象中了。
就是前文中经常提到的match object。
匹配对象总是有一个布尔值 True
。如果没有匹配的话 match() 和 search() 返回 None(判断时作为布尔值False)
所以你可以简单的用 if
语句来判断是否匹配
>>> pattern = re.compile("o")
>>> match = pattern.search("dog")
>>> if match:
... print("search 'o' in 'dog' OK")
...
search 'o' in 'dog' OK
Match.group([group1, ...])
返回一个或者多个匹配的子组。如果只有一个参数,结果就是一个字符串,如果有多个参数,结果就是一个元组(每个参数对应一个项),如果没有参数,组1默认到0(整个匹配都被返回)。 如果一个组N 参数值为 0,相应的返回值就是整个匹配字符串;如果它是一个范围 [1..99],结果就是相应的括号组字符串。如果一个组号是负数,或者大于样式中定义的组数,就引发一个 IndexError 异常。如果一个组包含在样式的一部分,并被匹配多次,就返回最后一个匹配。
>>> m = re.match(r"(\w+) (\w+)", "Isaac Newton, physicist, Bill Clark, sportsman")
>>> m.group()
'Isaac Newton'
>>> m.group(0)
'Isaac Newton'
>>> m.group(1)
'Isaac'
>>> m.group(2)
'Newton'
>>> m.group(1, 2)
('Isaac', 'Newton')
注意:
如果正则表达式使用了 (?P
语法, groupN 参数就也可能是命名组合的名字。
>>> m = re.match(r"(?P\w+) (?P\w+)", "Malcolm Reynolds")
>>> m.group('first_name')
'Malcolm'
>>> m.group('last_name')
'Reynolds'
命名组合同样可以通过索引值引用,使用索引时不需要引号
>>> m.group(1)
'Malcolm'
>>> m.group(2)
'Reynolds'
如果一个组匹配成功多次,这个组只返回最后一个匹配。
>>> m = re.match(r"(..)+", "a1b2c3")
>>> m.group(1)
'c3'
>>> print(m)
>>> m = re.match(r"(..)?", "a1b2c3") # This indicates'a1' can be matched
>>> m.group(1)
'a1'
>>> print(m)
注意:
>>> m = re.match(r"(\d+)", "20 40 60")
>>> m.group(1)
'20'
>>> m.group(2)
Traceback (most recent call last):
File "", line 1, in
IndexError: no such group
获取分组也可以用这种形式:m[0]/m[1]/m[2],或者m['name1']/m['name2']
Match.groups(default=None)
返回一个元组,包含所有匹配的子组,在样式中出现的从1到任意多的组合。
>>> m = re.match(r"(\d+)\.(\d+)", "24.1632")
>>> m.groups()
('24', '1632')
Match.groupdict(default=None)
以词典形式,返回所有的命名子组。词典的关键字为组名,元素为该组匹配的字符串。
>>> m = re.match(r"(?P\w+) (?P\w+)", "Malcolm Reynolds")
>>> m.groupdict()
{'first_name': 'Malcolm', 'last_name': 'Reynolds'}
Match.start([group])
Match.end([group])
返回匹配到的字符串的起始和结束位置。
如果没有group参数,或者group=0,则返回整个匹配的字符串的起止位置;否则返回对应组匹配的字符串的起止位置。例如上面的例子中,如果查看m的start()/end(),结果如下:
>>> m.start()
0
>>> m.end()
16
>>> m.start(1)
0
>>> m.end(1)
7
组有可能会匹配一个空字符串,此时这个组的start() = end()
>>> m = re.search('b(c?)', 'cba')
>>> m.start(0)
1
>>> m.end(0)
2
>>> m.start(1)
2
>>> m.end(1)
2
说明:
下面这个例子会从email地址中移除掉 remove_this
>>> email = "tony@tiremove_thisger.net"
>>> m = re.search("remove_this", email)
>>> email[:m.start()] + email[m.end():]
'[email protected]'
假设你在写一个扑克程序,一个玩家的一手牌为五个字符的串,每个字符表示一张牌,"a" 就是 A, "k" K, "q" Q, "j" J, "t" 为 10, "2" 到 "9" 表示2 到 9。实现如下需求的功能:
限制:
test_re.py文件源码如下:
import re
def displaymatch(match):
if match is None:
return None
print('' % (match.group(), match.groups()))
def check_pokers(pokers):
"""
"""
# check if pokers are valid
valid = re.compile(r"^[a2-9tjqk]{5}$")
valid_match = valid.match(pokers)
displaymatch(valid_match)
if valid_match is None:
print("pokers: %r not valid" % (pokers))
return
# if valid, check if pair exists
pair = re.compile(r".*(.).*\1")
pair_match = pair.match(pokers)
if pair_match is None:
print("pokers: %r has no pair" % (pokers))
return
# if pair exists, display which pair
print("pokers: %r has pair %r" % (pokers, pair_match.group(1)))
主要技术点就是:使用了对组的反向引用来检查是否存在对子,即正则表达式".*(.).*\1"。其中,这个表达式中的\1就是对组(.)的引用,称为反向引用。
导入这个模块,测试程序如下:
>>> import test_re
>>> test_re.check_pokers("727ak")
pokers: '727ak' has pair '7'
>>> test_re.check_pokers("akt5e")
pokers: 'akt5e' not valid
>>> test_re.check_pokers("aa788")
pokers: 'aa788' has pair '8'
>>> test_re.check_pokers("aa777")
pokers: 'aa777' has pair '7'
下表提供了 scanf()
格式符和正则表达式之间一些大致等价的映射。
|
正则表达式 |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
根据文本的特点,使用re.split()可以将内容拆分成表的形式。
例如,有如下文本,我们希望拆分成“姓名/电话/地址”这样的列表
>>> text = """Ross McFluff: 834.345.1254 155 Elm Street
...
... Ronald Heathmore: 892.345.3428 436 Finley Avenue
... Frank Burger: 925.541.7625 662 South Dogwood Way
...
...
...
... Heather Albrecht: 548.326.4584 919 Park Place"""
分析:先用换行符\n拆行,注意:每两行有效内容之间可能会有一至多个换行符,因此需要'\n+'。然后对每一行,按照": "或者" "来拆分,注意最后的地址部分的空格不能拆,可以通过设定最大拆分的次数来达到这个目的。
>>> entries = re.split("\n+", text)
>>> for entry in entries:
... print(entry)
...
Ross McFluff: 834.345.1254 155 Elm Street
Ronald Heathmore: 892.345.3428 436 Finley Avenue
Frank Burger: 925.541.7625 662 South Dogwood Way
Heather Albrecht: 548.326.4584 919 Park Place
>>> for entry in entries:
... for item in [re.split(":? ", entry, 3)]:
... print(item)
...
['Ross', 'McFluff', '834.345.1254', '155 Elm Street']
['Ronald', 'Heathmore', '892.345.3428', '436 Finley Avenue']
['Frank', 'Burger', '925.541.7625', '662 South Dogwood Way']
['Heather', 'Albrecht', '548.326.4584', '919 Park Place']
sub() 替换字符串中出现的样式的每一个实例。因此,可以用来做文字整理。同时,由于sub()函数的替换字符repl可以是函数,因此,可以设计非常强大的文字整理工具。
教材上的例子是:将每个单词除首尾之外的字母随机打乱顺序。
findall() 匹配样式 所有 的出现,不仅是像 search() 中的第一个匹配。比如,如果一个作者希望找到文字中的所有副词,他可能会按照以下方法用 findall()
>>> text = "He was carefully disguised but captured quickly by police."
>>> re.findall(r"[a-zA-z]+ly\b",text)
['carefully', 'quickly']
一个 词法器或词法分析器 分析字符串,并分类成目录组。 这是写一个编译器或解释器的第一步。
我们将class和功能函数tokenize()放到test_re.py这个模块中,将后面的测试程序放在交互窗口中。
test_re.py中代码如下:
import re
from typing import NamedTuple
class Token(NamedTuple):
type: str
value: str
line: int
column: int
def tokenize(code):
keywords = {'IF', 'THEN', 'ENDIF', 'FOR', 'NEXT', 'GOSUB', 'RETURN'}
token_specification = [
('NUMBER', r'\d+(\.\d*)?'), # Integer or decimal number
('ASSIGN', r':='), # Assignment operator
('END', r';'), # Statement terminator
('ID', r'[A-Za-z]+'), # Identifiers
('OP', r'[+\-*/]'), # Arithmetic operators
('NEWLINE', r'\n'), # Line endings
('SKIP', r'[ \t]+'), # Skip over spaces and tabs
('MISMATCH', r'.'), # Any other character
]
tok_regex = '|'.join('(?P<%s>%s)' % pair for pair in token_specification)
print(tok_regex)
line_num = 1
line_start = 0
for mo in re.finditer(tok_regex, code):
kind = mo.lastgroup
value = mo.group()
column = mo.start() - line_start
if kind == 'NUMBER':
value = float(value) if '.' in value else int(value)
elif kind == 'ID' and value in keywords:
kind = value
elif kind == 'NEWLINE':
line_start = mo.end()
line_num += 1
continue
elif kind == 'SKIP':
continue
elif kind == 'MISMATCH':
raise RuntimeError(f'{value!r} unexpected on line {line_num}')
yield Token(kind, value, line_num, column)
说明:
对上述代码的使用如下:
>>> statements = '''
... IF quantity THEN
... total := total + price * quantity;
... tax := price * 0.05;
... ENDIF;
... '''
>>> for token in tokenize(statements):
... print(token)
...
(?P\d+(\.\d*)?)|(?P:=)|(?P;)|(?P[A-Za-z]+)|(?P[+\-*/])|(?P\n)|(?P[ \t]+)|(?P.)
Token(type='IF', value='IF', line=2, column=0)
Token(type='ID', value='quantity', line=2, column=3)
Token(type='THEN', value='THEN', line=2, column=12)
Token(type='ID', value='total', line=3, column=2)
Token(type='ASSIGN', value=':=', line=3, column=8)
Token(type='ID', value='total', line=3, column=11)
Token(type='OP', value='+', line=3, column=17)
Token(type='ID', value='price', line=3, column=19)
Token(type='OP', value='*', line=3, column=25)
Token(type='ID', value='quantity', line=3, column=27)
Token(type='END', value=';', line=3, column=35)
Token(type='ID', value='tax', line=4, column=2)
Token(type='ASSIGN', value=':=', line=4, column=6)
Token(type='ID', value='price', line=4, column=9)
Token(type='OP', value='*', line=4, column=15)
Token(type='NUMBER', value=0.05, line=4, column=17)
Token(type='END', value=';', line=4, column=21)
Token(type='ENDIF', value='ENDIF', line=5, column=0)
Token(type='END', value=';', line=5, column=5)
正如前面所述,对tokenize()的使用,就是生成器的标准用法之一,"for xx in generator:"。