器→工具, 术→技巧, 研发, 编程语言

正则表达式与Python Re模块

钱魏Way · · 1,202 次浏览
!文章内容如有错误或排版问题,请提交反馈,非常感谢!

re模块是Python标准库中的一个模块,用于执行正则表达式操作。正则表达式是一种强大的工具,用于字符串匹配、查找、替换和解析。re模块提供了丰富的功能,支持复杂的模式匹配和字符串处理。

正则表达式简介

正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个”规则字符串”,这个”规则字符串”用来表达对字符串的一种过滤逻辑。正则表达式是用来匹配字符串非常强大的工具,在其他编程语言中同样有正则表达式的概念,Python同样不例外,利用了正则表达式,我们想要从返回的页面内容提取出我们想要的内容就易如反掌了。

下表列出了正则表达式模式语法中的特殊元素。如果你使用模式的同时提供了可选的标志参数,某些模式元素的含义会改变。

正则表达式模式

  • 普通字符:直接匹配自身,如a匹配字符a。
  • 元字符:具有特殊意义的字符,如.、*、?等。
  • 字符集:匹配字符集中的任何一个字符,如[abc]匹配a、b或c。
  • 预定义字符集:
    • \d:匹配任何数字,相当于[0-9]。
    • \D:匹配任何非数字字符。
    • \w:匹配任何字母数字字符或下划线,相当于[a-zA-Z0-9_]。
    • \W:匹配任何非字母数字字符。
    • \s:匹配任何空白字符,包括空格、制表符、换行符等。
    • \S:匹配任何非空白字符。

常用正则表达式操作符

  • ^:匹配字符串的开始。
  • $:匹配字符串的结束。
  • .:匹配除换行符以外的任何单个字符。
  • *:匹配前一个字符或子模式0次或多次。
  • +:匹配前一个字符或子模式1次或多次。
  • ?:匹配前一个字符或子模式0次或1次。
  • {m,n}:匹配前一个字符或子模式至少m次,至多n次。
  • |:匹配左右任意一个子模式(或条件)。
  • ():将子模式组合在一起,作为一个整体进行匹配,也用于捕获匹配的子组。

数量词的贪婪模式与非贪婪模式

正则表达式通常用于在文本中查找匹配的字符串。Python里默认是贪婪的,总是尝试匹配尽可能多的字符。我们一般使用非贪婪模式来提取。在我解释这个概念之前,我想先展示一个例子。我们要从一段html文本寻找锚标签:

import re

html = 'Hello<a href="https://www.biaodainfu.com">biaodianfu</a>'
m = re.findall('<a.*>.*<\/a>', html)
if m:
    print(m)

执行结果:

['<a href="https://www.biaodainfu.com">biaodianfu</a>']

我们改下输入,添加第二个锚标签:

import re
html = 'Hello<a href="https://www.biaodainfu.com">biaodianfu</a>|Hello<a href="https://www.google.com">Google</a>'
m = re.findall('<a.*>.*<\/a>', html)
if m:
    print(m)

执行结果:

['<a href="https://www.biaodainfu.com">biaodianfu</a>|Hello<a href="https://www.google.com">Google</a>']

貌似不是我们想要的啊,这次模式匹配了第一个开标签和最后一个闭标签以及在它们之间的所有的内容,成了一个匹配而不是两个单独的匹配。这是因为默认的匹配模式是”贪婪的”。

当处于贪婪模式时,量词(比如*和+)匹配尽可能多的字符。当你加一个问号在后面时(.*?)它将变为”非贪婪的”。

import re

html = 'Hello<a href="https://www.biaodainfu.com">biaodianfu</a>|Hello<a href="https://www.google.com">Google</a>'
m = re.findall('<a.*?>.*?<\/a>', html)
if m:
    print(m)

执行结果:

['<a href="https://www.biaodainfu.com">biaodianfu</a>', '<a href="https://www.google.com">Google</a>']

反斜杠问题

与大多数编程语言相同,正则表达式里使用”\”作为转义字符,这就可能造成反斜杠困扰。假如你需要匹配文本中的字符”\”,那么使用编程语言表示的正则表达式里将需要4个反斜杠”\\”:前两个和后两个分别用于在编程语言里转义成反斜杠,转换成两个反斜杠后再在正则表达式里转义成一个反斜杠。

Python里的原生字符串很好地解决了这个问题,这个例子中的正则表达式可以使用r”\”表示。同样,匹配一个数字的”\d”可以写成r”\d”。

Python Re中常见的方法

re.compile(pattern, flags=0)

Pattern(_sre.SRE_Pattern)对象是一个编译好的正则表达式,通过Pattern提供的一系列方法可以对文本进行匹配查找。Pattern不能直接实例化,必须使用re.compile()进行构造。

pattern = re.compile(r'hello')

re.compile中参数flag是匹配模式,匹配模式让你可以修改正则表达式的一些运行方式。在re模块中标志可以使用两个名字,一个是全名如IGNORECASE,一个是缩写,一字母形式如I。多个标志可以通过按位OR-ing它们来指定。如re.I|re.M被设置成I和M标志。可选值有:

  • I(全名:IGNORECASE): 使匹配对大小写不敏感,字符类和字符串匹配字母时忽略大小写。
  • L(全名:LOCALE): 使预定字符类\w\W\b\B\s\S取决于当前区域设定。(不常用)
  • M(全名:MULTILINE): 多行模式,改变’^’和’$’的行为。
  • S(全名:DOTALL): 点任意匹配模式,改变’.’的行为,使.匹配包括换行在内的所有字符。
  • X(全名:VERBOSE): 详细模式。这个模式下正则表达式可以是多行,忽略空白字符,并可以加入注释。
  • U(全名:UNICODE): 使得\w,\W,\b,\B,\d,\D,\s和\S取决于UNICODE定义的字符属性。

匹配模式可以是数字,要满足多个匹配模式,数字相加即可。

  • I = IGNORECASE = 2
  • L = LOCALE = 4
  • M = MULTILINE = 8
  • S = DOTALL = 16
  • U = UNICODE = 32
  • X = VERBOSE = 64

详细说明:

  • L:locales 是 C 语言库中的一项功能,是用来为需要考虑不同语言的编程提供帮助的。举个例子,如果你正在处理法文文本,你想用 \w+ 来匹配文字,但 \w 只匹配字符类 [A-Za-z];它并不能匹配 “é” 或 “ç”。如果你的系统配置适当且本地化设置为法语,那么内部的 C 函数将告诉程序 “é” 也应该被认为是一个字母。当在编译正则表达式时使用 LOCALE 标志会得到用这些 C 函数来处理 \w 后的编译对象;这会更慢,但也会象你希望的那样可以用 \w+ 来匹配法文文本。
  • M:使用 “^” 只匹配字符串的开始,而 $ 则只匹配字符串的结尾和直接在换行前(如果有的话)的字符串结尾。当本标志指定后,”^” 匹配字符串的开始和字符串中每行的开始。同样的,$ 元字符匹配字符串结尾和字符串中每行的结尾。
  • X:该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解。当该标志被指定时,在 RE 字符串中的空白符被忽略,除非该空白符在字符类中或在反斜杠之后;这可以让你更清晰地组织和缩进 RE。它也可以允许你将注释写入 RE,这些注释会被引擎忽略;注释用 “#” 号来标识,不过该符号不能在字符串或反斜杠之后。

Pattern 对象方法

Pattern.match(string, pos=0, endpos=None)

尝试从字符串的起始位置匹配模式,如果匹配成功,返回一个 Match 对象,否则返回 None。

  • string:要匹配的字符串。
  • pos:字符串的起始搜索位置。
  • endpos:字符串的结束搜索位置。

Pattern.search(string, pos=0, endpos=None)

扫描整个字符串,返回第一个匹配的 Match 对象,如果没有找到匹配项,返回 None。

Pattern.findall(string, pos=0, endpos=None)

返回字符串中所有与模式匹配的子串列表。

Pattern.finditer(string, pos=0, endpos=None)

返回一个迭代器,迭代匹配字符串中的所有匹配项,每项为一个 Match 对象。

Pattern.sub(repl, string, count=0)

将字符串中所有与模式匹配的部分替换为 repl,返回替换后的新字符串。

  • repl:替换的字符串或一个函数。
  • count:最大替换次数,默认为 0,表示替换所有匹配项。

Match 对象

当 re.match() 或 re.search() 成功匹配时,它会返回一个 Match 对象。Match 对象包含有关匹配结果的详细信息,并提供了一些方法来获取匹配信息。

Match 对象是一次匹配的结果,包含了很多关于此次匹配的信息,可以使用 Match 提供的可读属性或方法来获取这些信息。

属性:

  • string: 匹配时使用的文本。
  • re: 匹配时使用的 Pattern 对象。
  • pos: 文本中正则表达式开始搜索的索引。
  • endpos: 文本中正则表达式结束搜索的索引。
  • lastindex: 最后一个被捕获的分组在文本中的索引。如果没有被捕获的分组,将为 None。
  • lastgroup: 最后一个被捕获的分组的别名。如果这个分组没有别名或者没有被捕获的分组,将为 None。

方法:

  • group([group1,…]):获得一个或多个分组截获的字符串;指定多个参数时将以元组形式返回。group 可以使用编号也可以使用别名;编号 0 代表整个匹配的子串;不填写参数时,返回 group(0);没有截获字符串的组返回 None;截获了多次的组返回最后一次截获的子串。
  • groups([default]):以元组形式返回全部分组截获的字符串。相当于调用 group(1,2,…last)。default 表示没有截获字符串的组以这个值替代,默认为 None。
  • groupdict([default]):返回以有别名的组的别名为键、以该组截获的子串为值的字典,没有别名的组不包含在内。default 含义同上。
  • start([group]):返回指定的组截获的子串在 string 中的起始索引(子串第一个字符的索引)。group 默认值为 0。
  • end([group]):返回指定的组截获的子串在 string 中的结束索引(子串最后一个字符的索引+1)。group 默认值为 0。
  • span([group]):返回 (start(group), end(group))。
  • expand(template):将匹配到的分组代入 template 中然后返回。template 中可以使用 \id 或 \g<id>、\g<name> 引用分组,但不能使用编号 0。\id 与 \g<id> 是等价的;但 \10 将被认为是第 10 个分组,如果你想表达 \1 之后是字符 ‘0’,只能使用 \g<1>0。
# 示例:获取匹配信息
result = re.match(r'(\d+)-(\d+)', '123-456')
if result:
    print(result.group())  # 输出: 123-456
    print(result.group(1))  # 输出: 123
    print(result.group(2))  # 输出: 456
    print(result.span())  # 输出: (0, 7)

re.match(pattern, string, flags=0)

尝试从字符串的起始位置匹配模式。如果匹配成功,返回一个 Match 对象,否则返回 None。这是 Pattern.match() 的快捷方式。match 方法与 search 方法极其类似,区别在于 match() 函数只检测 re 是不是在 string 的开始位置匹配,search() 会扫描整个 string 查找匹配。

import re

# 示例:匹配字符串的开头
result = re.match(r'\d+', '123abc')
print(result.group())  # 输出: 123

re.search(pattern, string, flags=0)

扫描整个字符串,返回第一个匹配的 Match 对象,如果没有找到匹配项,返回 None。这是 Pattern.search() 的快捷方式。

# 示例:在整个字符串中搜索
result = re.search(r'\d+', 'abc123def')
print(result.group())  # 输出: 123

re.findall(pattern, string, flags=0)

找到 RE 匹配的所有子串,并把它们作为一个列表返回。这个匹配是从左到右有序地返回。如果无匹配,返回空列表。返回字符串中所有与模式匹配的子串列表。等价于 Pattern.findall()。

# 示例:查找所有匹配项
result = re.findall(r'\d+', 'abc123def456ghi789')
print(result)  # 输出: ['123', '456', '789']

re.finditer(pattern, string, flags=0)

返回一个迭代器,迭代匹配字符串中的所有匹配项,每项为一个 Match 对象。等价于 Pattern.finditer()。

#示例:迭代所有匹配项
matches = re.finditer(r'\d+', 'abc123def456')
for match in matches:
    print(match.group())  # 输出: 123, 456

re.split(pattern, string, maxsplit=0, flags=0)

通过正则表达式将字符串分离。如果用括号将正则表达式括起来,那么匹配的字符串也会被列入到list中返回。maxsplit是分离的次数,maxsplit=1分离一次,默认为0,不限制次数。

根据模式匹配的部分将字符串分割成列表。等价于Pattern.split()。

#示例:分割字符串
result = re.split(r'\d+', 'abc123def456ghi')
print(result)  # 输出: ['abc', 'def', 'ghi']

re.sub(pattern, repl, string, count=0, flags=0)

将字符串中所有与模式匹配的部分替换为repl,返回替换后的新字符串。等价于Pattern.sub()。

找到RE匹配的所有子串,并将其用一个不同的字符串替换。可选参数count是模式匹配後替换的最大次数;count必须是非负整数。缺省值是0表示替换所有的匹配。如果无匹配,字符串将会无改变地返回。

#示例:替换所有匹配项
result = re.sub(r'\d+', '#', 'abc123def456')
print(result)  # 输出: abc#def#

re.subn(pattern, repl, string, count=0, flags=0)

与re.sub方法作用一样,但返回的是包含新字符串和替换执行次数的两元组。

re.template(pattern, flags=0)

模版形式编译?没用过。也找不到更详细的资料。

re.escape(pattern)

可以对字符串中所有可能被解释为正则运算符的字符进行转义的应用函数。如果字符串很长且包含很多特殊技字符,而你又不想输入一大堆反斜杠,或者字符串来自于用户(比如通过raw_input函数获取输入的内容),且要用作正则表达式的一部分的时候,可以使用这个函数。

import re

print(re.escape('www.biaodianfu.com'))
##执行结果:
www\.biaodianfu\.com

re.purge()

清空缓存中的正则表达式参考资料

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注