资讯专栏INFORMATION COLUMN

Python中的正则表达式

Lowky / 3323人阅读

摘要:正则表达式匹配的字符串限定范围和否定除了单字符外,字符集还支持匹配指定的字符范围。问号要求正则表达式引擎在当前正则表达式中尽可能少地匹配字符。正则表达式匹配的字符串表示简单浮点数的字符串扩展表示法正则表达式的扩展表示法,以问号开始。

简介

正则表达式 是包含文本和特殊字符的字符串, 为高级的文本模式匹配, 抽取, 与文本形式的搜索和替换功能提供了基础

Python通过标准库re模块来支持正则表达式

模式匹配的两种方法完成匹配(模式匹配)

搜索(search())

匹配(match())

特殊符号和字符

元字符指正则表达式中的特殊符号和字符。

符号
符号 描述 示例
literal 匹配文本字符串的字面值literal foo
`re1 re2` 匹配正则表达式re1re2 `foo bar`
. 匹配任何字符(除 之外) f.o
^ 匹配字符串起始部分 ^foo
$ 匹配字符串终止部分 bar$
* 匹配0次或者多次前面出现的正则表达式 [A-Za-z0-9]*
+ 匹配1次或者多次前面出现的正则表达式 [a-z]+.com
? 匹配0次或者1次前面出现的正则表达式 goo?
{N} 匹配N次前面出现的正则表达式 [0-9]{3}
{M,N} 匹配M~N次前面出现的正则表达式 [0-9]{5,9}
[…] 匹配来自字符集的任意单一字符 [aeiou]
[x-y] 匹配x~y范围中的任意单一字符 [0-9], [A-Za-z]
[^…] 不匹配此字符集中出现的任何一个字符, 包括某一范围的字符 [^aeiou], [^A-Za-z]
`(* + ? {})?` 用于匹配上面频繁出现/重复出现符号的非贪婪版本(*、+、?、{}) .*?[a-z]
(…) 匹配封闭的正则表达式,然后另存为子组 `([0-9]{3})?,f(oo u)bar`
特殊符号
符号 描述 示例
d 匹配任何十进制数字,与[0-9]一致 datad+.txt
D d相反
w 匹配任何字母数字字符,与[A-Za-z0-9]相同
W w相反
s 匹配任何空格字符,与[ vf]相同
S s相反
N 匹配已保存的子组 N price:1
c 逐字匹配任何特殊字符c .,,*
A 匹配字符串起始,与^相同
 匹配字符串结束,与$相同
扩展符号
符号 描述 示例
(?iLmsux) 在正则表达式本身中嵌入一个或多个特殊特殊标记 (vs. via function/method) (?x),(?im)
(?:...) 匹配一个不用保存的分组 (?:w+.)
(?P...) 使用名字表示的正则分组 (?P)
(?#...) 表示注释,所有内容会被忽略 (?#comment)
(?=...) 匹配条件是如果...出现在之后的位置,而不使用输入字符串;称作正向前视断言(positive lookahead assertion) (?=.com)
(?!...) 匹配条件是如果...不出现在之后的位置,而不使用输入字符串;称作负向前视断言(negative lookahead assertion) (?!.net)
(?<=...) 匹配条件是如果...出现在之前的位置,而不使用输入字符串;称作正向后视断言(positive lookbehind assertion) (?<=800-)
(? 匹配条件是如果...不出现在之前的位置,而不使用输入字符串;称作负向后视断言(negative lookbehind assertion) (?
`(?(id/name)Y N)` Conditional match of regex Y if group with given id or name exists else N; N is optional `(?(1)y x)`
使用管道符匹配多个正则表达式

管道符号在正则表达式中又称为择一匹配符,表示 从多个模式中选择其一 的操作。

正则表达式 匹配的字符串
`at home` at,home
`r2d2 c3po` r2d2,c3po
`bat bet bit` bat,bet,bit
匹配任意单个字符

句点(.)符号匹配除了换行符 以外的任何字符。无论字母、数字、空格(不包括 换行符)、可打印字符、不可打印字符,使用.都可以匹配。

正则表达式 匹配的字符串
f.0 匹配在字母fo之间的任意一个字符,如:fao,f9o,f#o
.. 任意两个字符
.end 匹配在字符串end之前的任意一个字符

注意

要显式匹配一个句点符号本身,必须使用反斜线转义句点符号的功能,例如.

匹配起始或结尾

有些符号和相关的特殊字符用于在字符串的起始或结尾部分指定用于搜索的模式。

符号 位置
^A 起始
$ 结尾

简单示例如下。

正则表达式 匹配的字符串
^Froms 任何以From作为开头的字符串
/bin/bash$ 任何以/bin/bash结尾的字符串
^Subject:hi$ 匹配Subject:hi
匹配单词边界
符号 说明
 匹配一个单词的边界
B 匹配不是一个单词的边界

简单示例如下。

正则表达式 匹配的字符串
the 任何包含the的字符串
the 任何以the开始的字符串
the 仅仅匹配单词the
Bthe 任何包含但并不以the作为起始的字符串
创建字符集

使用[]创建字符集,可以匹配某些特定字符。

正则表达式 匹配的字符串
b[aeiu]t bat,bet,bit,but
[cr][23] c2,c3,r2,r3
限定范围和否定

除了单字符外,字符集还支持匹配指定的字符范围。两个字符中间用连字符-连接,用于指定一个字符的范围。如果^紧跟在左括号后面,这个符号就表示不匹配给定字符集中任何一个字符。

正则表达式 匹配的字符串
z.[0-9] z 后面跟着任何一个字符,然后跟着一个数字
[r-u][env-y][us] 等价于[rstu][envwxy][us] ,比如匹配res
[^aeiou] 匹配一个非元音字符
[^ ] 不匹配制表符或换行符
使用闭包操作符实现存在性和频数匹配
符号 说明
* 匹配左侧的正则表达式出现零次或多次的情形,这称作Kleene闭包
+ 匹配一次或多次出现的正则表达式,这称作正闭包操作符
? 操作符将匹配零次或者一次出现的正则表达式
{} 里面或者是单值,或者是一对由逗号分隔的数值;{N}表示匹配N次;{N,M}表示匹配N~M

如果问号紧跟在任何使用闭包操作符的匹配后面,它将直接要求正则表达式引擎匹配尽可能少的次数。当模式匹配使用分组操作符时,正则表达式引擎将试图吸收匹配该模式的尽可能多的字符,这通常叫做贪婪匹配。问号要求正则表达式引擎在当前正则表达式中尽可能少地匹配字符。

简单示例。

正则表达式 匹配的字符串
[dn]ot do,dot,no,not
0?[1-9] 可能存在前置0的数字
[0-9]{15,16} 匹配15或者16个数字
]+> 匹配HTML标签
表示字符集的特殊字符

有一些特殊字符能够表示字符集。

符号 描述
d 匹配任何十进制数字,与[0-9]一致 datad+.txt
D d相反
w 匹配任何字母数字字符,与[A-Za-z0-9]相同
W w相反
s 匹配任何空格字符,与[ vf]相同
S s相反

简单示例。

正则表达式 匹配的字符串
w+-d+ 一个由字母数字组成的字符串和一串由一个连字符分割的数字
使用圆括号指定分组

有时候不仅想要知道整个字符串是否匹配我们的标准,而且想要知道能否提取任何已经成功匹配的特定字符串或者子字符串,可以使用分组来实现。

一对圆括号可以实现一下任意一个功能:

对正则表达式进行分组

匹配子组

对正则表达式分组,可以在整个正则表达式中使用重复的操作符。

使用圆括号进行分组的一个副作用就是,匹配模式的子字符串可以保存起来供后续使用。这些子组能够被同一次的匹配或者搜索重复调用,或者提取出来用于后续处理。

匹配子组的重要性在于,很多时候除了进行匹配操作以外,我们还想要提取所匹配的模式。

简单示例如下。

正则表达式 匹配的字符串
d+(.d*)? 表示简单浮点数的字符串
扩展表示法

正则表达式的扩展表示法,以问号开始(?...)。他们通常用于在判断匹配之前提供标记,实现一个前视或者后视匹配,或者条件检查。

尽管这些符号使用圆括号,但是只有(?P) 表示一个分组匹配,其他的都没有创建一个分组。

正则表达式 匹配的字符串
(?:w+.)* 以句点作为结尾的字符串,例如 google.,但是这些匹配不会保存下来供后续使用和数据检索
(?#comment) 注释
(?=.com) 如果一个字符串和后面跟着 .com 才做匹配操作,并不使用任何目标字符串
(?!.net) 如果一个字符串后面不是跟着.net才做匹配操作
(?<=800-) 如果字符串之前为800-才做匹配
(? 如果一个字符串之前不是192.168.才做匹配操作
`(?(1)y x)` 如果一个匹配组1(1)存在,就与y匹配;否则与x匹配
Python中的正则表达式

在Python中,re模块支持更强大而且更通用的Perl风格的正则表达式,该模块允许多个线程共享同一个已编译的正则表达式对象,也支持命名子组。

re模块 re模块函数
函数 描述
compile(pattern, flags=0) 使用任何可选的标记来编译正则表达式的模式,然后返回一个正则表达式对象
re模块函数和正则表达式对象的方法
函数 描述 返回值
match(pattern, string, flags=0) 使用带有可选标记的正则表达式模式匹配字符串 匹配成功,返回匹配对象;如果失败,返回None
search(pattern, string, flags=0) 使用可选标记搜索字符串中第一次出现的正则表达式模式 匹配成功,返回匹配对象;如果失败,返回None
findall(pattern, string[, flags]) 查找字符串中所有(非重复)出现的正则表达式模式 匹配列表
finditer(pattern, string[, flags]) findall相同,但返回的不是列表 一个迭代器
split(pattern, string,max=0 ) 根据正则表达式的模式分隔符,split函数将字符串分割为列表,然后返回成功匹配的列表,分割最多操作max次,默认分割所有匹配成功的位置 分割后的列表
sub(pattern, repl, string, count=0) 使用repl替换count次正则表达式的模式在字符串中出现的位置;默认替换所有 替换操作数目
purge() 清除隐式编译的正则表达式模式;清除缓存
常用的匹配对象方法
函数 描述
group(num=0) 返回整个匹配对象;或者编号为num的特定子组
groups(default=None) 返回一个包含所有匹配子组的元组(如果没有成功匹配,则返回一个空元组)
groupdict(default=None) 返回一个包含所有匹配的命名子组的字典,所有的子组名称作为字典的键(如果没有匹配成功返回一个空元组)
常用的模块属性
属性 描述
re.I,re.IGNORECASE 不区分大小写的匹配
re.L,re.LOCALE 根据所使用的本地语言环境通过w,W,,B,s,S实现匹配
re.M,re.MULTILINE ^$分别匹配目标字符串中行的起始和结尾,而不是严格匹配整个字符串本身的起始和结尾
re.S,re.DOTALL . 通常匹配除了 之外的所有单个字符,该标记可以使.匹配换行符
re.X, re.VERBOSE 通过反斜线转义,否则所有空格加上#都被忽略
使用compile函数编译正则表达式

在Python中可以通过两种途径使用正则表达式:

re模块函数

调用编译后的正则表达式对象方法

在Python中由两类对象和正则表达式有关:

re.compile生成的表达式对象

匹配对象(成功调用 match()search() 之后返回的对象)

几乎所有的re模块函数都可以作为regex对象的方法。

可以通过按位或操作符(|)合并使用多个标记

可以使用(?FLAG)将标记嵌入到正则表达式

>>> re.match(r"(?i)the", "the")
<_sre.SRE_Match object; span=(0, 3), match="the">
>>> re.match(r"(?i)the", "The")
<_sre.SRE_Match object; span=(0, 3), match="The">
>>> re.match(r"the", "The")
>>>

匹配对象以及group()groups()方法

匹配对象是成功调用match()或者search()返回的对象。匹配对象有两个主要的方法: group()groups()

group()要么返回整个匹配对象,要么根据要求返回特定子组。groups()则仅返回一个包含唯一或者全部子组的元组。如果没有子组的要求,那么当group()仍返回整个匹配时,groups()返回一个空元组。

Python正则表达式允许命名匹配。

使用match方法匹配字符串

match方法试图从字符串的起始部分对模式进行匹配。

如果匹配成功,就返回一个匹配对象;如果匹配对象失败,就返回None。

匹配对象的group()方法能够用于显示那个成功的匹配。

Python 3.5.4rc1 (default, Jul 25 2017, 08:53:34) 
[GCC 6.4.0 20170704] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import re
>>> m = re.match("foo", "foo")
>>> if m is not None:
...     m.group()
... 
"foo"
>>> m
<_sre.SRE_Match object; span=(0, 3), match="foo">

只要模式从字符串的起始部分匹配,即使字符串比模式长,匹配也仍然能够成功。匹配结果是从较长字符串中抽取的匹配部分

>>> re.match("foo", "food on the table").group()
"foo"
使用search在字符串中查找模式

search函数在任意位置对给定正则表达式模式搜索第一次出现的匹配情况。如果搜索到成功的匹配,就会返回一个匹配对象;否则,返回None。

match相比,match只能从起始开始匹配,search可以匹配任意位置。

>>> m = re.match("foo", "seafood")
>>> m.group() if m is not None else print(m)
None
>>> m = re.search("foo", "seafood")
>>> m.group() if m is not None else print(m)
"foo"

上面代码,使用match匹配失败,使用search则匹配成功。

匹配多个字符串

管道符号或择一匹配符号(|)的使用。

>>> bt = "bat|bet|bit"
>>> m = re.match(bt, "bat")
>>> m.group() if m is not None else print(m)
"bat"
>>> m = re.match(bt, "blt")                   # 不能匹配
>>> m.group() if m is not None else print(m)
None
>>> m = re.match(bt, "He bit me")          # 不能匹配
>>> m.group() if m is not None else print(m)
None
>>> m = re.search(bt, "He bit me")      # 可以搜索到
>>> m.group() if m is not None else print(m)
"bit"
匹配任何单个字符

点号.不能匹配换行符和空字符串

>>> dote = ".end"
>>> m = re.match(dote, "bend")  # OK
>>> m.group() if m is not None else print(m)
"bend"
>>> m = re.match(dote, "end")   # 不能匹配空内容
>>> m.group() if m is not None else print(m)
None
>>> m = re.match(dote, "
end") # 不能匹配换行符
>>> m.group() if m is not None else print(m)
None
>>> m = re.search(dote, "The end.")  # 搜索匹配
>>> m.group() if m is not None else print(m)
" end"

使用转义符,匹配点号。

>>> pi = "3.14"
>>> pit = "3.14"
>>> m = re.match(pit, "3.14")   # 精确匹配
>>> m.group() if m is not None else print(m)
"3.14"
>>> m = re.match(pi, "3014")    # 点号匹配0
>>> m.group() if m is not None else print(m)
"3014"
>>> m = re.match(pi, "3.14")    # 点号匹配.
>>> m.group() if m is not None else print(m)
"3.14"
创建字符集
>>> selc = "[cr][23][dp][o2]"
>>> m = re.match(selc, "c3po")
>>> m.group() if m is not None else print(m)
"c3po"
>>> m = re.match(selc, "c3p1")
>>> m.group() if m is not None else print(m)
None
重复、特殊字符以及分组

正则表达式中最常见的情况包括:

特殊字符的使用

正则表达式模式的重复出现

使用圆括号对匹配模式的各部分进行分组和提取操作

特殊字符与重复出现

匹配0~1个中间子域名

>>> import re
>>> patt = "w+@(w+.)?w+.com"
>>> m = re.match(patt, "nobody@xxx.com")            # 匹配0个中间子域名
>>> m.group() if m is not None else print(m)
"nobody@xxx.com"
>>> m = re.match(patt, "nobody@xxx.yyy.com")        # 匹配1个中间子域名
>>> m.group() if m is not None else print(m)
"nobody@xxx.yyy.com"
>>> m = re.match(patt, "nobody@xxx.yyy.zzz.com")    # 不能匹配2个中间子域名
>>> m.group() if m is not None else print(m)
None

匹配任意多个子域名

>>> patt = "w+@(w+.)*w+.com"                    # 将 ? 替换为 *
>>> m = re.match(patt, "nobody@xxx.yyy.zzz.com")    # 匹配2个中间子域名
>>> m.group() if m is not None else print(m)
"nobody@xxx.yyy.zzz.com"

分组

使用圆括号来匹配和保存子组,以便于后续处理。

使用group()groups()方法获取分组,其两者区别:

group()

访问每个独立的子组

获取完整匹配(不传递参数)

groups()

获取一个包含所有匹配子组的元组

>>> m = re.match("(www)-(ffffd)", "abc-123")
>>> m.group()                   # 完整匹配
"abc-123"
>>> m.group(1)                  # 子组1
"abc"
>>> m.group(2)                  # 子组2
"123"
>>> m.groups()                  # 全部子组
("abc", "123")

一个完整示例

>>> m = re.match("ab", "ab")            # 没有分组
>>> m.group()                           # 完整匹配
"ab"
>>> m = re.match("ab", "ab")            # 没有分组
>>> m.group()                           # 完整匹配
"ab"
>>> m.groups()                          # 所有子组
()
>>>
>>> m = re.match("(ab)", "ab")          # 一个子组
>>> m.group()                           # 完整匹配
"ab"
>>> m.group(1)                          # 子组1
"ab"
>>> m.groups()                          # 全部子组
("ab",)
>>>
>>> m = re.match("(a)(b)", "ab")        # 两个子组
>>> m.group()
"ab"
>>> m.group(1)                          # 子组1
"a"
>>> m.group(2)                          # 子组2
"b"
>>> m.groups()                          # 全部子组
("a", "b")
>>>
>>> m = re.match("(a(b))", "ab")        # 两个嵌套子组
>>> m.group()                           # 完整匹配
"ab"
>>> m.group(1)                          # 子组1
"ab"
>>> m.group(2)                          # 子组2
"b"
>>> m.groups()                          # 全部子组
("ab", "b")
匹配字符串的起始和结尾以及单词边界
>>> m = re.search("^The", "The end.")   # 匹配
>>> m.group() if m is not None else print(m)
"The"
>>> m = re.search("^The", "end. The")   # 不做为开始
>>> m.group() if m is not None else print(m)
None
>>> m = re.search(r"the", "bite the dog")     # 匹配左侧边界
>>> m.group() if m is not None else print(m)
"the"
>>> m = re.search(r"the", "bitethe dog")      # 匹配左侧边界
>>> m.group() if m is not None else print(m)
None
>>> m = re.search(r"Bthe", "bitethe dog")      # 匹配左侧没有边界
>>> m.group() if m is not None else print(m)
"the"
>>> m = re.search(r"BtheB", "bitethe dog")    # 匹配两侧没有边界
>>> m.group() if m is not None else print(m)
None
>>> m = re.search(r"Bthe", "bitethe dog")    # 匹配左侧没有边界,右侧有边界
>>> m.group() if m is not None else print(m)
"the"
使用findall和finditer查找每一次出现的位置

findall()

查询字符串中某个正则表达式模式全部的非重复出现情况

match()search()的区别是,findall()总是返回一个列表

finditer()findall()类似,不过返回结果是一个迭代器。

>>> re.findall("car", "car")
["car"]
>>> re.findall("car", "carry")
["car"]
>>> re.findall("car", "carry the barcardi to the car")
["car", "car", "car"]

结合分组使用

>>> s = "This and that."
>>> re.findall(r"(thw+) and (thw+)", s, re.I)
[("This", "that")]
>>> list(re.finditer(r"(thw+) and (thw+)", s, re.I))[0].groups()
("This", "that")
>>> list(re.finditer(r"(thw+) and (thw+)", s, re.I))[0].group(1)
"This"
>>> list(re.finditer(r"(thw+) and (thw+)", s, re.I))[0].group(2)
"that"

单个分组的多重匹配

如果模式中只有一个分组,则匹配结果作为结果集合的单个元素

如果模式中由多个分组,则匹配结果为元组,作为结果集的单个元素

>>> s = "this and that"
>>> re.findall(r"(thw+) and (thw+)", s, re.I)
[("this", "that")]
>>> re.findall(r"(thw+)", s, re.I)
["this", "that"]
>>>
>>>
>>> s = "This and that. What, where, when, and who"
>>> re.findall(r"thw+|whw+", s, re.I)
["This", "that", "What", "where", "when", "who"]
>>> re.findall(r"(thw+)|(whw+)", s, re.I)
[("This", ""), ("that", ""), ("", "What"), ("", "where"), ("", "when"), ("", "who")]
>>> re.findall(r"(whw+)", s, re.I)
["What", "where", "when", "who"]
>>>
>>>
>>> s = "This where. That when. There who."
>>> re.findall(r"(thw+)s(whw+)", s, re.I)
[("This", "where"), ("That", "when"), ("There", "who")]
使用sub和subn搜索和替换

有两个函数用于实现搜索和替换功能: sub()subn()sub()返回一个替换后的字符串;subn()还返回一个表示替换的总数,替换后的字符串和替换总数作为元组返回。

>>> re.sub("X", "Mr. Smith", "attn: X

Dear X,
")
"attn: Mr. Smith

Dear Mr. Smith,
"
>>> re.subn("X", "Mr. Smith", "attn: X

Dear X,
")
("attn: Mr. Smith

Dear Mr. Smith,
", 2)
>>> re.sub("[ae]", "X", "abcdef")
"XbcdXf"
>>> re.subn("[ae]", "X", "abcdef")
("XbcdXf", 2)

使用匹配对象的group()方法除了能够取出匹配分组编号外,还可以使用N,其中N是在替换字符串中使用的分组编号。

>>> re.sub(r"(d{1,2})/(d{1,2})/(d{2}|d{4})", r"2/1/3", "2/20/1992")
"20/2/1992"
>>> re.sub(r"(d{1,2})/(d{1,2})/(d{2}|d{4})", r"2/1/3", "2/20/92")
"20/2/92"
在限定模式上使用split分隔字符串

split 基于正则表达式的模式分隔字符串。可以通过为max参数设定一个值(非零)来指定最大分割数。

>>> import re
>>> DATA = ()
>>> DATA = (
...     "Mountain View, CA 94040",
...     "Sunnyvale, CA",
...     "Los Altos, 94023",
...     "Cupertino 95014",
...     "Palo Alto CA"
... )
>>> for datum in DATA:
...     print(re.split(", |(?= (?:d{5}|[A-Z]{2})) ", datum))
...
["Mountain View", "CA", "94040"]
["Sunnyvale", "CA"]
["Los Altos", "94023"]
["Cupertino", "95014"]
["Palo Alto", "CA"]
扩展符号

通过使用(?iLmsux)系列选项,用户可以直接在正则表达式里面指定一个或者多个标记。

re.I/re.IGNORECASE, re.M/MULTILINE

>>> re.findall(r"(?i)yes", "yes? Yes. YES!~")        # 忽略大小写
["yes", "Yes", "YES"]
>>> re.findall(r"(?i)thw+", "The quickest way is through this tunnel.")    # 忽略大小写
["The", "through", "this"]
>>> re.findall(r"(?im)(^th[w ]+)", """                # 忽略大小写;多行
... This line is the first,
... another line,
... that line, it"s the best.
... """)
["This line is the first", "that line"]

re.S/re.DOTALL

使用re.S,re.DOTALL标记,使得点号.能够用来表示换行符。

>>> re.findall(r"th.+", """
... The first line
... the second line
... the third line
... """)
["the second line", "the third line"]
>>> re.findall(r"(?s)th.+", """
... The first line
... the second line
... the third line
... """)
["the second line
the third line
"]

re.X/re.VERBOSE

re.X/re.VERBOSE标记允许用户通过抑制在正则表达式中使用空白符(除了在字符类中或者在反斜线转义中)来创建更易读的正则表达式。

>>> re.search(r"""(?x)
...     ((d{3}))     # 匹配区号
...     [ ]             # 匹配空格
...     (d{3})         # 匹配前缀
...     -               # 横线
...     (d{4})         # 终点数字
... """, "(800) 555-1212").groups()
("800", "555", "1212")

(?:...)

通过使用(?:...)符号,可以对部分正则表达式进行分组,但是并不会保存该分组用于后续的检索或应用。

>>> re.findall(r"http://(?:w+.)*(w+.com)",
...             "http://goole.com http://www.google.com http://code.google.com")
["goole.com", "google.com", "google.com"]
>>> re.search(r"((?Pd{3})) (?Pd{3})-(?:d{4})",
...             "(800) 555-1212").groupdict()
{"areacode": "800", "prefix": "555"}

结合g,使用(?P)(?P=name)符号。

>>> re.sub(r"((?Pd{3})) (?Pd{3})-(?:d{4})",
...     "(g) g-xxxx", "(800) 555-1212")
"(800) 555-xxxx"

检索元组

对于没有命名的元组,使用N,其中N为数字

对于命名的元组,可以使用g,也可以使用N

>>> m = re.search(r"(d{4}) (?Pd{4}) (d{3})", "0530 8023 123")
>>> m.group()
"0530 8023 123"
>>> m.group(1)
"0530"
>>> m.group(2)
"8023"
>>> m.group(3)
"123"
>>> m.groupdict()
{"prefix": "8023"}
>>>
>>> re.sub(r"(d{4}) (d{4}) (d{3})", r"3 2 1", "0530 8023 123")
"123 8023 0530"
>>> re.sub(r"(d{4}) (?Pd{4}) (d{3})", r"3 2 1 - g", "0530 8023 123")
"123 8023 0530 - 8023"

在一个相同的正则表达式中重用模式

>>> re.match(r"(?Pd{2})(?P=num)", "1212")            # 只能匹配相同的内容
>>> print(m) if m is None else m.group()
"1212"
>>> m = re.match(r"(?Pd{2})(?P=num)", "1234")        # 不能匹配不同的内容
>>> print(m) if m is None else m.group()
None
>>> bool(re.match(r"((?Pd{3})) (?Pd{3})-(?Pd{4}) (?P=areacode)-(?P=prefix)-(?P=number)",
...     "(800) 555-1212 800-555-1212"))
True
>>> bool(re.match(r"""(?x)
...     # match (800) 555-1212, save areacode, prefix, no.
...     ((?Pd{3}))[ ](?Pd{3})-(?Pd{4})
... 
...     # space
...     [ ]
... 
...     # match 800-555-1212
...     (?P=areacode)-(?P=prefix)-(?P=number)
... 
...     # space
...     [ ]
... 
...     # match 18005551212
...     1(?P=areacode)(?P=prefix)(?P=number)
... """, "(800) 555-1212 800-555-1212 18005551212"))
True

前视匹配

可以使用(?=...)(?!...) 符号在目标字符串中实现一个前视匹配,而不必实际使用这些字符串。

(?=...) : 正向前视断言

(?!...) : 负向前视断言

>>> re.findall(r"w+(?= van Rossum)",    # 正向前视断言
... """
...     Guido van Rossum
...     Tim Peters
...     Alex Martelli
...     Just van Rossum
...     Raymond Hettinger
... """)
["Guido", "Just"]
>>> re.findall(r"(?m)^s+(?!noreply|postmaster)(w+)",    # 负向前视断言
... """
...     sales@phptr.com
...     postmaster@phptr.com
...     eng@phptr.com
...     noreply@phptr.com
...     admin@phptr.com
... """)
["sales", "eng", "admin"]
>>> ["%s@aw.com" % e.group(1) for e in 
... re.finditer(r"(?m)^s+(?!noreply|postmaster)(w+)",
... """
...     sales@phptr.com
...     postmaster@phptr.com
...     eng@phptr.com
...     noreply@phptr.com
...     admin@phptr.com
... """)]
["sales@aw.com", "eng@aw.com", "admin@aw.com"]

条件正则表达式匹配

(?(id/name)yes-pattern|no-pattern)

Will try to match with yes-pattern if the group with given id or name exists, and with no-pattern if it doesn’t. no-pattern is optional and can be omitted. For example, (<)?(w+@w+(?:.w+)+)(?(1)>|$) is a poor email matching pattern, which will match with "" as well as "user@host.com", but not with " nor "user@host.com>".
>>> r = r"(<)?(w+@w+(?:.w+)+)(?(1)>|$)"
>>> m = re.match(r, "")
>>> print(m) if m is None else m.group()
""
>>> m = re.match(r, "user@host.com")
>>> print(m) if m is None else m.group()
"user@host.com"
>>> m = re.match(r, ">> print(m) if m is None else m.group()
None
>>> m = re.match(r, "user@host.com>")
>>> print(m) if m is None else m.group()
贪婪搜索

加入由一系列类似如下格式的字符串

Thu Feb 15 17:32:12 2007::szhkai@qq.com::1123242-3-5

我们所感兴趣的是,数据记录内包含由连字符连接的三个整数的整行数据

>>> s = """
Thu Feb 15 17:41:42 2007::szhkai@qq.com::1123242-3
Sun Jul 22 13:32:25 2007::szhkai@qq.com::1123242-5
The May 12 17:02:52 2007::szhkai@qq.com::1123242-3-5
Thu Apr 18 12:22:42 2007::szhkai@qq.com::12323-3-5
"""
>>> re.findall(r"(?m).+d+-d+-d+", s)
["	The May 12 17:02:52 2007::szhkai@qq.com::1123242-3-5", "	Thu Apr 18 12:22:42 2007::szhkai@qq.com::12323-3-5"]

如果我们对d+-d+-d+这一部分感兴趣,可以使用元组提取

>>> re.findall(r"(?m).+(d+-d+-d+)", s)
["2-3-5", "3-3-5"]

但是不能提取第一个整数。这是因为正则表达式在实现上是采用贪婪匹配,试图匹配该模式尽可能多的字符。可以使用非贪婪操作符?解决这个问题。可以在*, +, ?后使用?。该操作符要求正则表达式引擎匹配尽可能少的字符。在.+后放置一个?可以获得期望的结果。

>>> re.findall(r"(?m).+?(d+-d+-d+)", s)
["1123242-3-5", "12323-3-5"]
注意事项

ASCII码冲突

如果符号同时使用于ASCII码和正则表达式特殊符号,就会出现问题,如表示ASCII字符的退格符,但是同时也是一个正则表达式的特殊符号,表示匹配一个单词的边界。对于正则表达式编译器而言,若将视为正则表达式特殊字符,需要使用进行转义。

>>> m = re.match("blow", "blow")      # 退格键; 没有匹配
>>> print(m) if m is None else m.group()
None
>>> m = re.match("blow", "blow")     # 匹配单词边界
>>> print(m) if m is None else m.group()
"blow"
>>> m = re.match(r"blow", "blow")     # 使用 raw string
>>> print(m) if m is None else m.group()
"blow"

w和W字母数字字符集同时受re.L/LOCALEUnicode(re.U/UNICODE)标记影响。

参考

《Python 核心编程》

Python文档

说明 Python版本
# 对于Python2
bovenson@ThinkCentre:~$ python2
Python 2.7.13+ (default, Jul 19 2017, 18:15:03) 
[GCC 6.4.0 20170704] on linux2
Type "help", "copyright", "credits" or "license" for more information.

# 对于Python3
bovenson@ThinkCentre:~$ python3
Python 3.5.4rc1 (default, Jul 25 2017, 08:53:34) 
[GCC 6.4.0 20170704] on linux
Type "help", "copyright", "credits" or "license" for more information.

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/44736.html

相关文章

  • Python基础教程:-正则达式基本语法以及re模块

    摘要:正则表达式关闭或可选标志。如果所含正则表达式,以表示,在当前位置成功匹配时成功,否则失败。否则指的是八进制字符码的表达式。 正则表达式是个很牛逼的东西,不管是在javascript,还是在Python web开发(http://www.maiziedu.com/course/python-px...)中,我们都会遇到正则表达式,虽然javascript和Python的正则表达式区别不大...

    y1chuan 评论0 收藏0
  • PythonPython正则达式使用指导

    摘要:反斜杠的困扰与大多数编程语言相同,正则表达式里使用作为转义字符,这就可能造成反斜杠困扰。文本中正则表达式结束搜索的索引。以元组形式返回全部分组截获的字符串。用于指定最大分割次数,不指定将全部分割。 1. 正则表达式基础 1.1. 简单介绍 正则表达式并不是Python的一部分。正则表达式是用于处理字符串的强大工具,拥有自己独特的语法以及一个独立的处理引擎,效率上可能不如str自带的方法...

    nihao 评论0 收藏0
  • python-文本处理和正则达式

    摘要:标记一个子表达式的开始和结束位置。用和进行搜索和替换二者几乎是一样的,都是将某字符串中所有匹配正则表达式模式的部分进行替换。 【未完成初稿】这周在工作中遇到很多需要从一段字符中增删查改的工作,第一遍用自己写的脚本去做,结果是可重用性不高,而且效率也不太好。其中也用到了python中的正则表达式部分re,后来就想能不能将我遇到的这类问题全部用正则去解决,具体就是python的re模块。答...

    dailybird 评论0 收藏0
  • Python正则达式很难?一篇文章搞定他,不是我吹!

    摘要:正则表达式语法字符与字符类特殊字符以上特殊字符要想使用字面值,必须使用进行转义字符类包含在中的一个或者多个字符被称为字符类,字符类在匹配时如果没有指定量词则只会匹配其中的一个。 1. 正则表达式语法 1.1 字符与字符类 1 特殊字符:.^$?+*{}| 以上特殊字符要想使用字面值,必须使用进行转义 2 字符类 1. 包含在[]中的一个或者多个字符被称为字符类,字符类在匹配时如果没有指...

    dcr309duan 评论0 收藏0
  • 使用Python正则达式操作文本数据

    摘要:在中,使用的内置模块处理正则表达式操作。使用正则表达式对象在字符串中搜索模式。编写和使用正则表达式在中创建正则表达式的第一步是导入模块正则表达式使用模式字符串表示,模式字符串是指定所需搜索模式的字符串。 showImg(https://segmentfault.com/img/remote/1460000019166945?w=504&h=300); 来源 | 愿码(ChainDes...

    Yuanf 评论0 收藏0
  • Python 正则达式 re 模块简明笔记

    摘要:假设现在想把字符串你好,,世界中的中文提取出来,可以这么做你好,,世界注意到,我们在正则表达式前面加上了两个前缀,其中表示使用原始字符串,表示是字符串。本文标题为正则表达式模块简明笔记本文链接为参考资料正则表达式 简介 正则表达式(regular expression)是可以匹配文本片段的模式。最简单的正则表达式就是普通字符串,可以匹配其自身。比如,正则表达式 hello 可以匹配字符...

    lastSeries 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<