python正则模块re
一.re模块内置的函数方法
re.compile(pattern, flags=0)
https://cdn.analyticsvidhya.com/wp-content/uploads/2019/06/seq2seq.gif)
re.compile()方法可以把一个正则表达式编译成一个正则对象(PatternObj),返回的正则对象是操作其他处理字符串方法的主体。
pattern_obj = re.compile(pattern)
match_obj = pattern_obj.compile(string)
等同于:
match_obj = re.match(pattern,string)
实际上re.match()处理流程内含re.compile()的过程。match方法源码:
def match(pattern, string, flags=0):
"""Try to apply the pattern at the start of the string, returning
a Match object, or None if no match was found."""
return _compile(pattern, flags).match(string)
可以看出match方法返回的实际就是正则对象pattern_obj调用match()方法的结果。
re.search(pattern, string, flags=0)
re.search()方法是搜索整个字符串,找到第一个符合正则规则的字符串部分,返回一个匹配对象(MatchObject);没有匹配成功,就返回None。
a= 'ababb_ijfknb'
b=re.search(r'((a|b)+)((\w)+)',a)
b.group(0)
#Out[22]: 'ababb_ijfknb'
b.group(1)
#Out[23]: 'ababb'
b.group(2)
#Out[24]: 'b'
b.group(3)
#Out[25]: '_ijfknb'
关于re.search当进行分组时,group(0) 就是整个匹配的内容,小括号代表分组,上述代码:group(1)是第一组即第一个小括号里面匹配的内容,当小括号里面嵌套了括号,会将最后在string匹配的内容作为一个组,然后是最后一个组。
re.match(pattern, string, flags=0)
re.match()方法是从字符串开始位置匹配整个字符串,当从字符串开始成功匹配到部分字符内容,返回一个匹配对象(MatchObject);没有匹配成功,就返回None。
re.search()和re.match()区别对比:位置上,search()方法可以从字符串任意位置匹配部分字符串内容 match()方法必须从字符串开始位置匹配字符串内容,一旦开头匹配不成,则匹配失败;内容上,search()方法是非贪婪匹配,只要找到第一个符合正则规则的部分字符串就返回匹配对象,match()方法则是按照正则规则只匹配字符串开始位置的部分字符串;多行模式下,match()方法依旧只会匹配字符串的开始位置,而search()方法和“^”联合使用则是从多行的每一行开始匹配。
re.split(pattern, string, maxsplit=0, flags=0)
re.split()表示对字符串string,按照正则表达式pattern匹配内容分隔字符串,其中maxsplit是指最大分隔次数,最大分隔次数应该是小于默认分隔次数的。分隔后的字符串内容组成列表返回。 如果在 pattern 中捕获到括号,那么所有的组里的文字也会包含在列表里。
import re
split_list_default = re.split(r'\W+', 'Words, words, words.')
print(split_list_default)
# ['Words', 'words', 'words', ''] 正则表达式\W+表示以一个或多个非单词字符对字符串分隔,分隔后组成列
#表的形式返回,注意列表后空字符串为'.'和之前的words分隔结果
split_list_max = re.split(r'\W+', 'Words, words, words.', 1)
print(split_list_max)
# ['Words', 'words, words.'] 指定分隔次数,字符串分隔会由左至右按照maxsplit最大分隔次数分隔,实际最大分隔次数是小于等于默认分隔次数的
split_list_couple = re.split(r'(\W+)', 'Words, words, words.')
print(split_list_couple)
# ['Words', ', ', 'words', ', ', 'words', '.', '']
#正则表达式中存在分组情况,即捕获型括号,(\W+)会捕获字符串中‘, ’并添加至列表一起显示出来
split_list_couple = re.split(r'(?:\W+)', 'Words, words, words.')
#?:开头将分隔符不显示
print(split_list_couple)
#['Words', 'words', 'words', '']
带分组的split方法
b = 'a12ass12asa123'
re.split(r'(1|2)',b)
#Out[7]: ['a', '1', '', '2', 'ass', '1', '', '2', 'asa', '1', '', '2', '3']
#同时出现1和2时,12中间的‘’当做占位 被显示出来
re.split(r'((1|2))',b)
Out[8]:
['a','1','1', '','2', '2','ass','1', '1', '','2', '2', 'asa','1', '1','','2', '2','3']
#这里讲分隔符号显示了两次 ,两组括号
b = 'a1fyu2342ajbui8542ss12asa123'
re.split(r'(?:1|2)',b)
#Out[54]: ['a', 'fyu', '34', 'ajbui854', 'ss', '', 'asa', '', '3']
re.findall(pattern, string, flags=0)
re.findall()类似于re.search()方法,re.search()是在字符串中搜索到第一个与正则表达式匹配的字符串内容就返回一个匹配对象MatchObject,而re.findall()方法是在字符串中搜索并找到所有与正则表达式匹配的字符串内容,组成一个列表返回,列表中元素顺序是按照正则表达式在字符串中由左至右匹配的返回;未匹配成功,返回一个空列表。
import re
pattern = r'\d{3}'
find = re.findall(pattern, 'include21321exclude13243alert213lib32')
print(find)
#注意:findall是将匹配的内容得到,以匹配后数值index+1位置继续进行寻找
# ['213', '132', '213']
#所以,本次并没有对于21321得到213,132,321序列,而是213,然后从3后面的2出发继续寻找,得到132和213
注意
: 当re.findall()中的正则表达式存在两个或两个以上分组时,按照分组自左向右的形式匹配,匹配结果按照顺序组成元组,返回列表中元素以元组的形式给出。a = 'Frank Burger: 925.541.7625 662 South Dogwood Way,Ronald Heathmore: 892.345.3428436 Finley Avenue' re.findall(r'[a-z]+ [a-z]+: (\d{3}\.\d{3}\.\d{4})',a,re.I) #返回分组之后内容 #Out[70]: ['925.541.7625', '892.345.3428']
import re
pattern = r'(\d{3})(1)'
find = re.findall(pattern, 'include21321exclude13243alert213lib32')
print(find)
re.finditer(pattern, string, flags=0)
re.finditer()相似于re.findall()方法,搜索字符串中所有与正则表达式匹配的字符串内容, 返回一个迭代器Iterator ,迭代器Iterator内保存了所有匹配字符串内容生成的匹配对象MatchObject。即匹配文本封装在匹配对象MatchObject中,多个匹配对象MatchObject保存在一个迭代器Iterator中。
import re
pattern = r'\d{3}'
find = re.finditer(pattern, 'include21321exclude13243alert213lib32')
print(find)
for i in find:
print(i)
print(i.group(0))
# <callable_iterator object at 0x00000000028FB0F0>
# <re.Match object; span=(7, 10), match='213'>
# 213
# <re.Match object; span=(19, 22), match='132'>
# 132
# <re.Match object; span=(29, 32), match='213'>
# 213
re.sub(pattern, repl, string, count=0, flags=0)
re.sub()表示用正则表达式匹配字符串string中的字符串内容,使用repl参数内容替换匹配完成的字符串内容,返回替换后的字符串。参数count指定替换次数,正则表达式匹配字符串是由左至右的,可能匹配多个内容,替换操作也是自左向右替换,如果只想替换左边部分匹配内容可以设置count参数,参数值为非负整数且小于等于最大匹配成功个数;未匹配成功,不做替换,返回原字符串。
import re
pattern = r'\d+'
find_default = re.sub(pattern, ' ', 'include21321exclude13243alert213lib32')
print(find_default)
find_count = re.sub(pattern, ' ', 'include21321exclude13243alert213lib32', 2)
print(find_count)
# include exclude alert lib
# include exclude alert213lib32
二 、正则表达式语法
一个正则表达式(或RE)指定了一集与之匹配的字符串;模块内的函数可以让你检查某个字符串是否跟给定的正则表达式匹配(或者一个正则表达式是否匹配到一个字符串,这两种说法含义相同)。
正则表达式可以拼接; 如果 A 和 B 都是正则表达式, 那么 AB 也是正则表达式。 通常, 如果字符串 p 匹配 A 并且另一个字符串 q 匹配 B, 那么 pq 可以匹配 AB。除非 A 或者 B 包含低优先级操作,A 和 B 存在边界条件;或者命名组引用。所以,复杂表达式可以很容易的从这里描述的简单源语表达式构建。 了解更多正则表达式理论和实现,参考the Friedl book [Frie09] ,或者其他编译器构建的书籍。
以下是正则表达式格式的简要说明。更详细的信息和演示,参考 正则表达式HOWTO。
正则表达式可以包含普通或者特殊字符。绝大部分普通字符,比如 'A'
, 'a'
, 或者 '0'
,都是最简单的正则表达式。它们就匹配自身。你可以拼接普通字符,所以 last
匹配字符串 'last'
. (在这一节的其他部分,我们将用 this special style
这种方式表示正则表达式,通常不带引号,要匹配的字符串用 'in single quotes'
,单引号形式。)
有些字符,比如 '|'
或者 '('
,属于特殊字符。 特殊字符既可以表示它的普通含义, 也可以影响它旁边的正则表达式的解释。
重复修饰符 (*
, +
, ?
, {m,n}
, 等) 不能直接嵌套。这样避免了非贪婪后缀 ?
修饰符,和其他实现中的修饰符产生的多义性。要应用一个内层重复嵌套,可以使用括号。 比如,表达式 (?:a{6})*
匹配6个 'a'
字符重复任意次数。
特殊字符是:
-
.
(点) 在默认模式,匹配除了换行的任意字符。如果指定了标签
DOTALL
,它将匹配包括换行符的任意字符。 -
^
(插入符号) 匹配字符串的开头, 并且在
MULTILINE
模式也匹配换行后的首个符号。 -
$
匹配字符串尾或者换行符的前一个字符,在
MULTILINE
模式匹配换行符的前一个字符。foo
匹配'foo'
和'foobar'
, 但正则foo$
只匹配'foo'
。更有趣的是, 在'foo1\nfoo2\n'
搜索foo.$
,通常匹配'foo2'
,但在MULTILINE
模式 ,可以匹配到'foo1'
;在'foo\n'
搜索$
会找到两个空串:一个在换行前,一个在字符串最后。 -
*
对它前面的正则式匹配0到任意次重复, 尽量多的匹配字符串。
ab*
会匹配'a'
,'ab'
, 或者'a'``后面跟随任意个 ``'b'
。 -
+
对它前面的正则式匹配1到任意次重复。
ab+
会匹配'a'
后面跟随1个以上到任意个'b'
,它不会匹配'a'
。 -
?
对它前面的正则式匹配0到1次重复。
ab?
会匹配'a'
或者'ab'
。 -
*?
,+?
,??
'*'
,'+'
,和'?'
修饰符都是 贪婪的;它们在字符串进行尽可能多的匹配。有时候并不需要这种行为。如果正则式<.*>
希望找到' b '
,它将会匹配整个字符串,而不仅是''
。在修饰符之后添加?
将使样式以 非贪婪方式或者 :dfn:
最小 方式进行匹配; 尽量 少 的字符将会被匹配。 使用正则式<.*?>
将会仅仅匹配''
。 -
{m}
对其之前的正则式指定匹配 m 个重复;少于 m 的话就会导致匹配失败。比如,
a{6}
将匹配6个'a'
, 但是不能是5个。 -
{m,n}
对正则式进行 m 到 n 次匹配,在 m 和 n 之间取尽量多。 比如,
a{3,5}
将匹配 3 到 5个'a'
。忽略 m 意为指定下界为0,忽略 n 指定上界为无限次。 比如a{4,}b
将匹配'aaaab'
或者1000个'a'
尾随一个'b'
,但不能匹配'aaab'
。逗号不能省略,否则无法辨别修饰符应该忽略哪个边界。 -
{m,n}?
前一个修饰符的非贪婪模式,只匹配尽量少的字符次数。比如,对于
'aaaaaa'
,a{3,5}
匹配 5个'a'
,而a{3,5}?
只匹配3个'a'
。 -
\
转义特殊字符(允许你匹配
'*'
,'?'
, 或者此类其他),或者表示一个特殊序列;特殊序列之后进行讨论。如果你没有使用原始字符串(r'raw'
)来表达样式,要牢记Python也使用反斜杠作为转义序列;如果转义序列不被Python的分析器识别,反斜杠和字符才能出现在字符串中。如果Python可以识别这个序列,那么反斜杠就应该重复两次。这将导致理解障碍,所以高度推荐,就算是最简单的表达式,也要使用原始字符串。 -
[]
用于表示一个字符集合。在一个集合中:字符可以单独列出,比如
[amk]
匹配'a'
,'m'
, 或者'k'
。可以表示字符范围,通过用'-'
将两个字符连起来。比如[a-z]
将匹配任何小写ASCII字符,[0-5][0-9]
将匹配从00
到59
的两位数字,[0-9A-Fa-f]
将匹配任何十六进制数位。 如果-
进行了转义 (比如[a\-z]
)或者它的位置在首位或者末尾(如[-a]
或[a-]
),它就只表示普通字符'-'
。特殊字符在集合中,失去它的特殊含义。比如[(+*)]
只会匹配这几个文法字符'('
,'+'
,'*'
, or')'
。字符类如\w
或者\S
(如下定义) 在集合内可以接受,它们可以匹配的字符由ASCII
或者LOCALE
模式决定。不在集合范围内的字符可以通过 取反 来进行匹配。如果集合首字符是'^'
,所有 不 在集合内的字符将会被匹配,比如[^5]
将匹配所有字符,除了'5'
,[^^]
将匹配所有字符,除了'^'
.^
如果不在集合首位,就没有特殊含义。在集合内要匹配一个字符']'
,有两种方法,要么就在它之前加上反斜杠,要么就把它放到集合首位。比如,[()[\]{}]
和[]()[{}]
都可以匹配括号。Unicode Technical Standard #18 里的嵌套集合和集合操作支持可能在未来添加。这将会改变语法,所以为了帮助这个改变,一个FutureWarning
将会在有多义的情况里被raise
,包含以下几种情况,集合由'['
开始,或者包含下列字符序列'--'
,'&&'
,'~~'
, 和'||'
。为了避免警告,需要将它们用反斜杠转义。在 3.7 版更改: 如果一个字符串构建的语义在未来会改变的话,一个FutureWarning
会raise
。 -
|
A|B
, A 和 B 可以是任意正则表达式,创建一个正则表达式,匹配 A 或者 B. 任意个正则表达式可以用'|'
连接。它也可以在组合(见下列)内使用。扫描目标字符串时,'|'
分隔开的正则样式从左到右进行匹配。当一个样式完全匹配时,这个分支就被接受。意思就是,一旦 A 匹配成功, B 就不再进行匹配,即便它能产生一个更好的匹配。或者说,'|'
操作符绝不贪婪。 如果要匹配'|'
字符,使用\|
, 或者把它包含在字符集里,比如[|]
. -
(...)
(组合),匹配括号内的任意正则表达式,并标识出组合的开始和结尾。匹配完成后,组合的内容可以被获取,并可以在之后用
\number
转义序列进行再次匹配,之后进行详细说明。要匹配字符'('
或者')'
, 用\(
或\)
, 或者把它们包含在字符集合里:[(]
,[)]
. -
(?…)
这是个扩展标记法 (一个
'?'
跟随'('
并无含义)。'?'
后面的第一个字符决定了这个构建采用什么样的语法。这种扩展通常并不创建新的组合;(?P...)
是唯一的例外。 以下是目前支持的扩展。 -
(?aiLmsux)
(
'a'
,'i'
,'L'
,'m'
,'s'
,'u'
,'x'
中的一个或多个) 这个组合匹配一个空字符串;这些字符对正则表达式设置以下标记re.A
(只匹配ASCII字符),re.I
(忽略大小写),re.L
(语言依赖),re.M
(多行模式),re.S
(点dot匹配全部字符),re.U
(Unicode匹配), andre.X
(冗长模式)。 (这些标记在 模块内容 中描述) 如果你想将这些标记包含在正则表达式中,这个方法就很有用,免去了在re.compile()
中传递 flag 参数。标记应该在表达式字符串首位表示。 -
(?:…)
正则括号的非捕获版本。 匹配在括号内的任何正则表达式,但该分组所匹配的子字符串 不能 在执行匹配后被获取或是之后在模式中被引用。
-
(?aiLmsux-imsx:…)
(
'a'
,'i'
,'L'
,'m'
,'s'
,'u'
,'x'
中的0或者多个, 之后可选跟随'-'
在后面跟随'i'
,'m'
,'s'
,'x'
中的一到多个 .) 这些字符为表达式的其中一部分 设置 或者 去除 相应标记re.A
(只匹配ASCII),re.I
(忽略大小写),re.L
(语言依赖),re.M
(多行),re.S
(点匹配所有字符),re.U
(Unicode匹配), andre.X
(冗长模式)。(标记描述在 模块内容 .)'a'
,'L'
and'u'
作为内联标记是相互排斥的, 所以它们不能结合在一起,或者跟随'-'
。 当他们中的某个出现在内联组中,它就覆盖了括号组内的匹配模式。在Unicode样式中,(?a:...)
切换为 只匹配ASCII,(?u:...)
切换为Unicode匹配 (默认). 在byte样式中(?L:...)
切换为语言依赖模式,(?a:...)
切换为 只匹配ASCII (默认)。这种方式只覆盖组合内匹配,括号外的匹配模式不受影响。*3.6 新版功能.*在 3.7 版更改: 符号'a'
,'L'
和'u'
同样可以用在一个组合内。 -
(?P…)
(命名组合)类似正则组合,但是匹配到的子串组在外部是通过定义的 name 来获取的。组合名必须是有效的Python标识符,并且每个组合名只能用一个正则表达式定义,只能定义一次。一个符号组合同样是一个数字组合,就像这个组合没有被命名一样。命名组合可以在三种上下文中引用。如果样式是
(?P['"]).*?(?P=quote)
(也就是说,匹配单引号或者双引号括起来的字符串):引用组合 “quote” 的上下文引用方法在正则式自身内(?P=quote)
(如示)\1
处理匹配对象 mm.group('quote')``m.end('quote')
(等)传递到re.sub()
里的 repl 参数中\g``\g<1>``\1
-
(?P=name)
反向引用一个命名组合;它匹配前面那个叫 name 的命名组中匹配到的串同样的字串。
-
(?#…)
注释;里面的内容会被忽略。
-
(?=…)
匹配
…
的内容,但是并不消费样式的内容。这个叫做 lookahead assertion。比如,Isaac (?=Asimov)
匹配'Isaac '
只有在后面是'Asimov'
的时候。 -
(?!…)
匹配
…
不符合的情况。这个叫 negative lookahead assertion (前视取反)。比如说,Isaac (?!Asimov)
只有后面 不 是'Asimov'
的时候才匹配'Isaac '
。 -
(?<=…)
匹配字符串的当前位置,它的前面匹配
…
的内容到当前位置。这叫:dfn:positive lookbehind assertion (正向后视断定)。(?<=abc)def
会在'abcdef'
中找到一个匹配,因为后视会往后看3个字符并检查是否包含匹配的样式。包含的匹配样式必须是定长的,意思就是abc
或a|b
是允许的,但是a*
和a{3,4}
不可以。注意以 positive lookbehind assertions 开始的样式,如(?<=abc)def
,并不是从 a 开始搜索,而是从 d 往回看的。 -
`(?
匹配当前位置之前不是
...
的样式。这个叫 negative lookbehind assertion (后视断定取非)。类似正向后视断定,包含的样式匹配必须是定长的。由 negative lookbehind assertion 开始的样式可以从字符串搜索开始的位置进行匹配。 -
(?(id/name)yes-pattern|no-pattern)
如果给定的 id 或 name 存在,将会尝试匹配
yes-pattern
,否则就尝试匹配no-pattern
,no-pattern
可选,也可以被忽略。比如,(<)?(\w+@\w+(?:\.\w+)+)(?(1)>|$)
是一个email样式匹配,将匹配''
或'user@host.com'
,但不会匹配' ,也不会匹配
‘user@host.com>’`。
由 '\'
和一个字符组成的特殊序列在以下列出。 如果普通字符不是ASCII数位或者ASCII字母,那么正则样式将匹配第二个字符。比如,\$
匹配字符 '$'
.
-
\number
匹配数字代表的组合。每个括号是一个组合,组合从1开始编号。比如
(.+) \1
匹配'the the'
或者'55 55'
, 但不会匹配'thethe'
(注意组合后面的空格)。这个特殊序列只能用于匹配前面99个组合。如果 number 的第一个数位是0, 或者 number 是三个八进制数,它将不会被看作是一个组合,而是八进制的数字值。在'['
和']'
字符集合内,任何数字转义都被看作是字符。 -
\A
只匹配字符串开始。
-
\b
匹配空字符串,但只在单词开始或结尾的位置。一个单词被定义为一个单词字符的序列。注意,通常
\b
定义为\w
和\W
字符之间,或者\w
和字符串开始/结尾的边界, 意思就是r'\bfoo\b'
匹配'foo'
,'foo.'
,'(foo)'
,'bar foo baz'
但不匹配'foobar'
或者'foo3'
。默认情况下,Unicode字母和数字是在Unicode样式中使用的,但是可以用ASCII
标记来更改。如果LOCALE
标记被设置的话,词的边界是由当前语言区域设置决定的,\b
表示退格字符,以便与Python字符串文本兼容。 -
\B
匹配空字符串,但 不 能在词的开头或者结尾。意思就是
r'py\B'
匹配'python'
,'py3'
,'py2'
, 但不匹配'py'
,'py.'
, 或者'py!'
.\B
是\b
的取非,所以Unicode样式的词语是由Unicode字母,数字或下划线构成的,虽然可以用ASCII
标志来改变。如果使用了LOCALE
标志,则词的边界由当前语言区域设置。 -
\d
对于 Unicode (str) 样式:匹配任何Unicode十进制数(就是在Unicode字符目录[Nd]里的字符)。这包括了
[0-9]
,和很多其他的数字字符。如果设置了ASCII
标志,就只匹配[0-9]
。对于8位(bytes)样式:匹配任何十进制数,就是[0-9]
。 -
\D
匹配任何非十进制数字的字符。就是
\d
取非。 如果设置了ASCII
标志,就相当于[^0-9]
。 -
\s
对于 Unicode (str) 样式:匹配任何Unicode空白字符(包括
[ \t\n\r\f\v]
,还有很多其他字符,比如不同语言排版规则约定的不换行空格)。如果ASCII
被设置,就只匹配[ \t\n\r\f\v]
。对于8位(bytes)样式:匹配ASCII中的空白字符,就是[ \t\n\r\f\v]
。 -
\S
匹配任何非空白字符。就是
\s
取非。如果设置了ASCII
标志,就相当于[^ \t\n\r\f\v]
。 -
\w
对于 Unicode (str) 样式:匹配Unicode词语的字符,包含了可以构成词语的绝大部分字符,也包括数字和下划线。如果设置了
ASCII
标志,就只匹配[a-zA-Z0-9_]
。对于8位(bytes)样式:匹配ASCII字符中的数字和字母和下划线,就是[a-zA-Z0-9_]
。如果设置了LOCALE
标记,就匹配当前语言区域的数字和字母和下划线。 -
\W
匹配任何不是单词字符的字符。 这与
\w
正相反。 如果使用了ASCII
旗标,这就等价于[^a-zA-Z0-9_]
。 如果使用了LOCALE
旗标,则会匹配在当前区域设置中不是字母数字又不是下划线的字符。 -
\Z
只匹配字符串尾。
-
re.``A
-
re.``ASCII
让
\w
,\W
,\b
,\B
,\d
,\D
,\s
和\S
只匹配ASCII,而不是Unicode。这只对Unicode样式有效,会被byte样式忽略。相当于前面语法中的内联标志(?a)
。注意,为了保持向后兼容,re.U
标记依然存在(还有他的同义re.UNICODE
和嵌入形式(?u)
) , 但是这些在 Python 3 是冗余的,因为默认字符串已经是Unicode了(并且Unicode匹配不允许byte出现)。 -
re.``DEBUG
显示编译时的debug信息,没有内联标记。
-
re.``I
-
re.``IGNORECASE
进行忽略大小写匹配;表达式如
[A-Z]
也会匹配小写字符。Unicode匹配(比如Ü
匹配ü
)同样有用,除非设置了re.ASCII
标记来禁用非ASCII匹配。当前语言区域不会改变这个标记,除非设置了re.LOCALE
标记。这个相当于内联标记(?i)
。注意,当设置了IGNORECASE
标记,搜索Unicode样式[a-z]
或[A-Z]
的结合时,它将会匹配52个ASCII字符和4个额外的非ASCII字符: ‘İ’ (U+0130, 拉丁大写的 I 带个点在上面), ‘ı’ (U+0131, 拉丁小写没有点的 I ), ‘ſ’ (U+017F, 拉丁小写长 s) and ‘K’ (U+212A, 开尔文符号).如果使用ASCII
标记,就只匹配 ‘a’ 到 ‘z’ 和 ‘A’ 到 ‘Z’ 。 -
re.``L
-
re.``LOCALE
由当前语言区域决定
\w
,\W
,\b
,\B
和大小写敏感匹配。这个标记只能对byte样式有效。这个标记不推荐使用,因为语言区域机制很不可靠,它一次只能处理一个 "习惯”,而且只对8位字节有效。Unicode匹配在Python 3 里默认启用,并可以处理不同语言。 这个对应内联标记(?L)
。在 3.6 版更改:re.LOCALE
只能用于byte样式,而且不能和re.ASCII
一起用。在 3.7 版更改: 设置了re.LOCALE
标记的编译正则对象不再在编译时依赖语言区域设置。语言区域设置只在匹配的时候影响其结果。 -
re.``M
-
re.``MULTILINE
设置以后,样式字符
'^'
匹配字符串的开始,和每一行的开始(换行符后面紧跟的符号);样式字符'$'
匹配字符串尾,和每一行的结尾(换行符前面那个符号)。默认情况下,’^’
匹配字符串头,'$'
匹配字符串尾。对应内联标记(?m)
。 -
re.``S
-
re.``DOTALL
让
'.'
特殊字符匹配任何字符,包括换行符;如果没有这个标记,'.'
就匹配 除了 换行符的其他任意字符。对应内联标记(?s)
。 -
re.``X
-
re.``VERBOSE
这个标记允许你编写更具可读性更友好的正则表达式。通过分段和添加注释。空白符号会被忽略,除非在一个字符集合当中或者由反斜杠转义,或者在
*?
,(?:
or(?P<…>
分组之内。当一个行内有#
不在字符集和转义序列,那么它之后的所有字符都是注释。意思就是下面两个正则表达式等价地匹配一个十进制数字:a = re.compile(r"""\d + # the integral part \. # the decimal point \d * # some fractional digits""", re.X) b = re.compile(r"\d+\.\d*")
python正则表达式中group
转载龙虾天天 最后发布于2019-07-05 14:41:35 阅读数 839 收藏
1 正则表达式中的(…)
用于匹配括号内的任何正则表达式,并且指明组的开始和结束位置;可以在执行匹配之后检索组中的内容,并且可以在可以在字符串中使用\number来进行进一步的匹配,如下所述。当需要匹配字符’(‘或者’)’时,可以使用(和) ,或者[(]和[)]来实现。
以上便是Python3.6官方文档对于(…)表达式的解说,该括号表达式用于定义一个group,一个正则表达式中可以有多个括号表达式,这就意味着匹配结果中可能有多个group,我们可以用group函数来定位到特定的group结果。
2 match.group([group1,…])
返回匹配结果中一个或多个group.如果该group函数仅仅有一个参数,那么结果就是单个字符串;如果有多个参数,结果是每一个参数对应的group项的元组.如果没有参数,那么参数group1默认为0(返回的结果就是整个匹配结果).如果参数值是0,那么返回整个匹配结果的字符串;如果它是[1…99]之间的数字,则返回的是与对应括号组匹配的字符串(第一节中已经解释了,正则表达式中的一个括号对应着一个group).如果组号为负或大于模式中定义的组数,则会引发IndexError异常。如果某个group包含在匹配模式中但没有找到相应的匹配,那么对应的结果就是None.如果一个group是匹配模式中的一部分,并且匹配到了多次,那么最后一次匹配结果将返回。
group和groups是两个不同的函数。
一般,m.group(N) 返回第N组括号匹配的字符。
而m.group()
== m.group(0)
== 所有匹配的字符,与括号无关,这个是API规定的。m.groups() 返回所有括号匹配的字符,以tuple格式。
m.groups() == (m.group(1), m.group(2) ...)
import re
#定义了两个group,因为包含两个括号
m = re.match("(\w+) (\w+)", "Isaac Newton, physicist")
#group(0)就是匹配的整个结果
print(m.group(0)) #输出结果为Isaac Newton
#group(1)是第一个group的值
print(m.group(1)) #输出结果为Isaac
#group(2)是第二个group的值
print(m.group(2)) #输出结果为Newton
#groups返回所有的group,以元组的形式
print(m.groups()) #输出结果为('Isaac','Newton')
1234567891011121314151617
3 findall(pattern, string, flag)
返回字符串中模式的所有非重叠匹配,结果以一个字符串list返回.字符串匹配过程是从左到右开始扫描,并且匹配结果按顺序返回,如果该模式组存在一个或多个匹配,则返回匹配该组的一个列表;如果模式中有多个group,则返回的是一个元组的列表,元组中的元素依次对应各组的匹配结果.空的匹配包含在结果中除非它们开始了一个新的匹配过程。
可以看出,findall实质上也是一个group的匹配过程.其结果就是groups匹配结果的列表.
import re
if __name__ == '__main__':
#用于测试的字符串
str = "<h1>liuwei</h1><a href='www.baidu.com'></a><h1>zhangbin</h1><a href='www.love.com'></a>"
regex = re.compile("<h1>(.+?)</h1><a(.+?)></a>") #定义了两个group,两个括号
res = regex.search(str) #search用于找到第一个满足匹配的子串,并返回
print("group1:%s" %res.group(1)) #输出结果为liuwei
print("group2:%s" %res.group(2)) #输出结果为href='www.baidu.com'
res1 = regex.findall(str) #findall输出所有满足的匹配
print("res1:%s" %res1)
print(res1[0]) #输出结果为('liuwei', 'www.baidu.com')
print(res1[1]) #输出结果为('zhangbin, 'www.love.com')
三 、 实例
检查对子
例子
我们使用以下辅助函数来更好地显示匹配对象:
def displaymatch(match):
if match is None:
return None
return '<Match: %r, groups=%r>' % (match.group(), match.groups())
假设你在写一个扑克程序,一个玩家的一手牌为五个字符的串,每个字符表示一张牌,“a” 就是 A, “k” K, “q” Q, “j” J, “t” 为 10, “2” 到 “9” 表示2 到 9。
要看给定的字符串是否有效,我们可以按照以下步骤
ma4 = re.search(r'<([\w]+>)[\w]+</\1','<book>python</book>')
#\1表示使用编号为1的分组,在前面有个括号([\w]+>) 这个括号内的就表示是编号为1的分组,如果这个正则表达式中#有多个括号,就是说有多个分组,然后想复用第n个分组,就加一个\n,就OK了,不知道有没有说清楚
ma4.group()
Out[63]: '<book>python</book>'
ma4.groups()
Out[64]: ('book>',)
>>> valid = re.compile(r"^[a2-9tjqk]{5}$")
>>> displaymatch(valid.match("akt5q")) # Valid.
"<Match: 'akt5q', groups=()>"
>>> displaymatch(valid.match("akt5e")) # Invalid.
>>> displaymatch(valid.match("akt")) # Invalid.
>>> displaymatch(valid.match("727ak")) # Valid.
"<Match: '727ak', groups=()>"
最后一手牌,"727ak"
,包含了一个对子,或者两张同样数值的牌。要用正则表达式匹配它,应该使用向后引用如下
>>>
>>> pair = re.compile(r".*(.).*\1")
>>> displaymatch(pair.match("717ak")) # Pair of 7s.
"<Match: '717', groups=('7',)>"
>>> displaymatch(pair.match("718ak")) # No pairs.
>>> displaymatch(pair.match("354aa")) # Pair of aces.
"<Match: '354aa', groups=('a',)>"
要找出对子由什么牌组成,开发者可以按照下面的方式来使用匹配对象的 group()
方法:
>>>
>>> pair = re.compile(r".*(.).*\1")
>>> pair.match("717ak").group(1)
'7'
# Error because re.match() returns None, which doesn't have a group() method:
>>> pair.match("718ak").group(1)
Traceback (most recent call last):
File "<pyshell#23>", line 1, in <module>
re.match(r".*(.).*\1", "718ak").group(1)
AttributeError: 'NoneType' object has no attribute 'group'
>>> pair.match("354aa").group(1)
'a'
模拟 scanf()
Python 目前没有一个类似c函数 scanf()
的替代品。正则表达式通常比 scanf()
格式字符串要更强大一些,但也带来更多复杂性。下面的表格提供了 scanf()
格式符和正则表达式大致相同的映射。
scanf() 格式符 |
正则表达式 |
---|---|
%c |
. |
%5c |
.{5} |
%d |
[-+]?\d+ |
%e , %E , %f , %g |
[-+]?(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)? |
%i |
[-+]?(0[xX][\dA-Fa-f]+|0[0-7]*|\d+) |
%o |
[-+]?[0-7]+ |
%s |
\S+ |
%u |
\d+ |
%x , %X |
[-+]?(0[xX])?[\dA-Fa-f]+ |
从文件名和数字提取字符串
/usr/sbin/sendmail - 0 errors, 4 warnings
你可以使用 scanf()
格式化
%s - %d errors, %d warnings
等价的正则表达式是:
(\S+) - (\d+) errors, (\d+) warnings
search() vs. match()
Python 提供了两种不同的操作:基于 re.match()
检查字符串开头,或者 re.search()
检查字符串的任意位置(默认Perl中的行为)。
例如
>>>
>>> re.match("c", "abcdef") # No match
>>> re.search("c", "abcdef") # Match
<re.Match object; span=(2, 3), match='c'>
在 search()
中,可以用 '^'
作为开始来限制匹配到字符串的首位
>>>
>>> re.match("c", "abcdef") # No match
>>> re.search("^c", "abcdef") # No match
>>> re.search("^a", "abcdef") # Match
<re.Match object; span=(0, 1), match='a'>
注意 MULTILINE
多行模式中函数 match()
只匹配字符串的开始,但使用 search()
和以 '^'
开始的正则表达式会匹配每行的开始
>>>
>>> re.match('X', 'A\nB\nX', re.MULTILINE) # No match
>>> re.search('^X', 'A\nB\nX', re.MULTILINE) # Match
<re.Match object; span=(4, 5), match='X'>
建立一个电话本
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"""
条目用一个或者多个换行符分开。现在我们将字符串转换为一个列表,每个非空行都有一个条目:
>>> entries = re.split("\n+", text)
>>> entries
['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']
最终,将每个条目分割为一个由名字、姓氏、电话号码和地址组成的列表。我们为 split()
使用了 maxsplit
形参,因为地址中包含有被我们作为分割模式的空格符:
>>> [re.split(":? ", entry, 3) for entry in entries]
[['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']]
:?
样式匹配姓后面的冒号,因此它不出现在结果列表中。如果 maxsplit
设置为 4
,我们还可以从地址中获取到房间号:
>>> [re.split(":? ", entry, 4) for entry in entries]
[['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()
来整理文字,或者随机化每个字符的位置,除了首位和末尾字符
>>>
>>> def repl(m):
... inner_word = list(m.group(2))
... random.shuffle(inner_word)
... return m.group(1) + "".join(inner_word) + m.group(3)
>>> text = "Professor Abdolmalek, please report your absences promptly."
>>> re.sub(r"(\w)(\w+)(\w)", repl, text)
'Poefsrosr Aealmlobdk, pslaee reorpt your abnseces plmrptoy.'
>>> re.sub(r"(\w)(\w+)(\w)", repl, text)
'Pofsroser Aodlambelk, plasee reoprt yuor asnebces potlmrpy.'
找到所有副词
findall()
匹配样式 所有 的出现,不仅是像 search()
中的第一个匹配。比如,如果一个作者希望找到文字中的所有副词,他可能会按照以下方法用 findall()
>>>
>>> text = "He was carefully disguised but captured quickly by police."
>>> re.findall(r"\w+ly", text)
['carefully', 'quickly']
找到所有副词和位置
如果需要匹配样式的更多信息, finditer()
可以起到作用,它提供了 匹配对象 作为返回值,而不是字符串。继续上面的例子,如果一个作者希望找到所有副词和它的位置,可以按照下面方法使用 finditer()
>>>
>>> text = "He was carefully disguised but captured quickly by police."
>>> for m in re.finditer(r"\w+ly", text):
... print('%02d-%02d: %s' % (m.start(), m.end(), m.group(0)))
07-16: carefully
40-47: quickly
原始字符记法
原始字符串记法 (r"text"
) 保持正则表达式正常。否则,每个正则式里的反斜杠('\'
) 都必须前缀一个反斜杠来转义。比如,下面两行代码功能就是完全一致的
>>>
>>> re.match(r"\W(.)\1\W", " ff ")
<re.Match object; span=(0, 4), match=' ff '>
>>> re.match("\\W(.)\\1\\W", " ff ")
<re.Match object; span=(0, 4), match=' ff '>
当需要匹配一个字符反斜杠,它必须在正则表达式中转义。在原始字符串记法,就是 r"\\"
。否则就必须用 "\\\\"
,来表示同样的意思
>>>
>>> re.match(r"\\", r"\\")
<re.Match object; span=(0, 1), match='\\'>
>>> re.match("\\\\", r"\\")
<re.Match object; span=(0, 1), match='\\'>
写一个词法分析器
一个 词法器或词法分析器 分析字符串,并分类成目录组。 这是写一个编译器或解释器的第一步。
文字目录是由正则表达式指定的。这个技术是通过将这些样式合并为一个主正则式,并且循环匹配来实现的
import collections
import re
Token = collections.namedtuple('Token', ['type', 'value', 'line', 'column'])
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)
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)
这个词法器产生以下输出
Token(type='IF', value='IF', line=2, column=4)
Token(type='ID', value='quantity', line=2, column=7)
Token(type='THEN', value='THEN', line=2, column=16)
Token(type='ID', value='total', line=3, column=8)
Token(type='ASSIGN', value=':=', line=3, column=14)
Token(type='ID', value='total', line=3, column=17)
Token(type='OP', value='+', line=3, column=23)
Token(type='ID', value='price', line=3, column=25)
Token(type='OP', value='*', line=3, column=31)
Token(type='ID', value='quantity', line=3, column=33)
Token(type='END', value=';', line=3, column=41)
Token(type='ID', value='tax', line=4, column=8)
Token(type='ASSIGN', value=':=', line=4, column=12)
Token(type='ID', value='price', line=4, column=15)
Token(type='OP', value='*', line=4, column=21)
Token(type='NUMBER', value=0.05, line=4, column=23)
Token(type='END', value=';', line=4, column=27)
Token(type='ENDIF', value='ENDIF', line=5, column=4)
Token(type='END', value=';', line=5, column=9)
转载:https://blog.csdn.net/weixin_41358871/article/details/105059417