正则表达式是编程过程中进行字符串模式识别、字符串处理的强力工具。学过一些自动机理论就知道正则表达式其实是有穷自动机,其可以识别的字符串集合称为其语言。正则表达式使用的是一种线性的、非递归的文法,并提供有限的上下文有关文法。
符号
- 字面值表达式
就是普通的,没有带任何标记的字符串
text = r'this is a sample text'
print(re.findall(r'is a',text))
for i in re.finditer(r'is a',text):
print(i.group(),i.start())
<<< ['is a']
<<< is a 5
- 或表达式
re1|re2
用于匹配两个正则语言的并集
下面例子使用'-' 或 '|'来分割字符串
text = r'123/123-4212-4242/23543'
for item in re.split(r'(?:/|-)',text):
print(item)
<<< 123
<<< 123
<<< 4212
<<< 4242
<<< 23543
-
.
匹配任何字符(除了\n 之外)
text = r'123/123-42abc12-4242/23543'
print(re.findall(r'-.......-',text))
<<< ['-42abc12-']
-
^
匹配字符串起始部分
text = r'123qwe123'
for it in re.finditer(r'^123',text):
print(it.start())
<<< 0
-
$
匹配字符串终止部分
text = r'123qwe123'
for it in re.finditer(r'123$',text):
print(it.start())
<<< 6
-
*
匹配 0 次或者多次前面出现的正则表达式
text = r'''\
Cool Things
Cool Things
CoolThings
'''
print(re.findall(r'\w\w\w\w\s*\w\w\w\w\w\w',text))
<<< ['Cool Things', 'Cool Things', 'CoolThings']
Cool 和 Things,之间可以有一个或多个空格,或者没有
-
+
匹配 1 次或者多次前面出现的正则表达式
text = r'''\
Cool Things
Cool Things
CoolThings
'''
print(re.findall(r'\w\w\w\w\s+\w\w\w\w\w\w',text))
<<< ['Cool Things', 'Cool Things']
Cool 和 Things,之间可以有一个或多个空格
-
?
匹配 0 次或者 1 次前面出现的正则表达式
text = r'''\
Cool Things
Cool Things
CoolThings
'''
print(re.findall(r'\w\w\w\w\s?\w\w\w\w\w\w',text))
<<< ['Cool Things', 'CoolThings']
Cool 和 Things,之间可以有一个空格,或者没有
-
{N}
匹配 N 次前面出现的正则表达式
将前面的大量重复改善
text = r'''\
Cool Things
Cool Things
CoolThings
'''
print(re.findall(r'\w{4}\s*\w{6}',text))
<<< ['Cool Things', 'Cool Things', 'CoolThings']
-
{M,N}
匹配 M~N 次前面出现的正则表达式
text = '''\
www.baidu.com
www.google.com
www.360.com
www.errorformat
'''
for it in re.finditer(r'www(\.\w+){2,3}',text):
print(it.group())
<<< www.baidu.com
<<< www.google.com
<<< www.360.com
-
[…]
匹配来自字符集的任意单一字符
找出元音字母的位置
text = '''\
I believe, for every drop of rain
that falls, A flower grows...
I believe that somewhere in the
darkest night, A candle glows...
'''
for it in re.finditer(r'(?i)[aeiou]',text):
print(it.start(),end=' ')
<<< 0 3 5 6 8 12 15 17 23 26 30 31 36 40 46 50 52 57 64 67 69 70 72 76 80 82 85 87 89 94 97 100 105 111 114 118 122
-
[..x−y..]
匹配 x ~ y 范围中的任意单一字符
找出一串中能形成8进制数的字符串
text = '''\
adas a1290 21469
12412 762 10923
aeqw
'''
for it in re.finditer(r'\b[0-7]+\b',text):
print(it.group(),end=' ')
<<< 12412 762
-
[^…]
不匹配此字符集中出现的任何一个字符,包括某一范围的字符(如果在此字符集中出现)
找出不含数字的非空白单词
text = '''\
adas a1290 21469
12412 762 10923
aeqw
'''
print(repr(text))
for it in re.finditer(r'\b[^0-9\s]+\b',text):
print(it.group(),end=' ')
<<< adas aeqw
-
(…)
匹配封闭的正则表达式,然后另存为子组
有一点组合设计模式的味道,() 内也是完整的正则表达式,可以用于提取子组
text = '''\
11/27/1997
5/22/1998
2/29/1996
'''
print(repr(text))
for it in re.finditer(r'(\d+)/(\d+)/(\d+)',text):
print(it.group(3),'年',it.group(1),'月',it.group(2),'日')
<<< 1997 年 11 月 27 日
<<< 1998 年 5 月 22 日
<<< 1996 年 2 月 29 日
-
(*|+|?|{})?
用于匹配上面频繁出现/重复出现符号的非贪婪版本 (*、+、?、{})
其行为与贪婪相反,贪婪匹配会尽可能多的匹配字符,回溯的时候再减少匹配的字符。
非贪婪匹配会尽可能少的匹配,回溯的时候再增加匹配的字符。
text = '''\
3.1415926
3.14
'''
print(repr(text))
for it in re.finditer(r'\d+\.[\d]{4,6}?',text):
print(it.group())
<<< 3.1415
特殊字符
前面出现了很多形如 \w \b \d 的特殊符号,下面总结:
-
\d
匹配任何十进制数字,与[0-9]一致(\D
与\d
相反,不匹配任何非数值型的数字)如:
data\d+.txt
匹配 data1.txt 、data2.txt …… -
\w
匹配任何字母数字字符,与[A-Za-z0-9_]
相同(\W
与之相反)如: 标识符表示为
[a-zA-Z_]\w+
-
\s
匹配任何空格字符,与[\n\t\r\v\f]
相同(\S
与之相反)如:
You\sare\sthe\winner
-
\b
匹配任何单词边界(\B
与之相反)如:
\bThe\b
\N
匹配已保存的子组N
(参见上面的(…))
匹配第二个数字以第一个数字为前缀的字符串
text = '''\
3.14 3.1415926
'''
m = re.match(r'(\d+\.\d+)\s+(\1\d+)',text)
if m is not None:
print(m.group(1),m.group(2))
<<< 3.14 3.1415926
-
\c
逐字匹配任何特殊字符c
(即,仅按照字面意义匹配,不匹配特殊含义)c
包括 . \ ' [ ] { } ( ) ? + * 等用于正则表达式的标记符号 -
\A(\Z)
匹配字符串的起始(结束)(另见上面介绍的^
和$
)如:
\ADear
扩展正则表达式
-
(?iLmsux)
在正则表达式中嵌入一个或者多个特殊“标记”参数(或者通过函数/方法)- 使用时
(?...)
是一体的,置于正则表达式的最左端 -
(?i)
用于忽略大小写,如前所示的元音字母就使用了r'(?i)[aeiou]'
-
(?m)
实现跨行搜索,而不是将字符串看做整体,可以使^&
匹配每一行的首尾 -
(?s)
使得.
可以匹配换行符\n -
(?x)
该标记允许用户通过抑制在正则表达式中使用空白符(除了在字符类中或者在反斜线转义中)来创建更易读的正则表达式。
- 使用时
-
(?:…)
表示一个匹配不用保存的分组使用该标记后,分组将不会被保存到matchObj.groups()中
-
(?P
像一个仅由 name 标识而不是数字 ID 标识的正则分组匹配…) 该标记可以给分组起名字
(?P=name)
在同一字符串中匹配由(?P=name)分组的之前文本
改写之前的例子
text = '''\
3.14 3.1415926
'''
m = re.match(r'(?P\d+\.\d+)\s+((?P=prefix)\d+)',text)
if m is not None:
print(m.group(1),m.group(2))
<<< 3.14 3.1415926
在sub
方法中可以用g
来检索分组名
text = '''\
11/27/1997\
'''
res = re.sub(r'(?P\d+)/(?P\d+)/(?P\d+)',r'\g/\g/\g',text)
print(res)
<<< 27/11/1997
(?#…)
表示注释,所有内容都被忽略(?=…)
匹配条件是如果…出现在之后的位置,而不使用输入字符串;称作正向前视断言
意思是匹配的时候向前尝试匹配后缀,如果后缀匹配成功,则匹配前缀
text = '''\
11/27/1997\
'''
for it in re.finditer(r'\d{1,2}/\d{1,2}(?=/\d{4})',text):
print(it.group())
<<< 11/27
-
(?!…)
匹配条件是如果…不出现在之后的位置,而不使用输入字符串;称作负向前视断言
与(?=…)
相反,如果后缀不能匹配,这匹配前缀
-
(?<=…)
匹配条件是如果…出现在之前的位置,而不使用输入字符串;称作正向后视断言
检查前缀,如果前缀匹配,则匹配后缀
text = '''\
ABC12345 qd7654\
'''
print(text)
print()
for it in re.finditer(r'(?<=[a-zA-Z]{3})\d+',text):
print(it.group())
<<< 12345
-
(? 匹配条件是如果…不出现在之前的位置,而不使用输入字符串;称作负向后视断言
检查前缀,如果前缀不匹配,则匹配后缀
-
(?(id/name)Y|N )
如果分组所提供的id
或者name
(名称)存在,就返回正则表达式的条件匹配Y
,如果不存在,就返回N
;|N
是可选项