正则表达式是一个很强大的字符串处理工具,它能帮助我们方便的检查一个字符串是否与某种模式匹配。
一、python中正则表达式的基本语法规则
序号 | 语法 | 解释 | 表达式 | 成功匹配对象 |
---|---|---|---|---|
1 | 一般字符 | 匹配自身相对应的字符 | abc | abc |
2 | . | 匹配除换行符(\n)以外的任意字符 | a.c | abc |
3 | \ | 转义字符,可以改变原字符的意思 | a.c | a.c |
4 | \d | 匹配数字:0~9 | \dabc | 1abc |
5 | \w | 匹配单词字符,a~z;A~Z;0~9 | \w\w\w | oX2 |
6 | \s | 匹配空格字符(\t,\n,\r,\f,\v) | a\sc | a c |
7 | \D | 匹配非数字字符 | \Dabc | aabc |
8 | \W | 匹配非单词字符 | a\Wc | a c |
9 | \S | 匹配非空格字符 | \S\Sc | 1bc |
10 | [] | 字符集,对应位置上可以是字符集里的任意字符 | a[def]c | aec |
11 | [^] | 对字符集当中的内容进行取反 | a[^def]c | a2c |
12 | [a-z] | 指定一个范围字符集 | a[A-Z]c | aBc |
13 | * | 允许前一个字符可以出现0次或者无限次 | a*b | aaab或b |
14 | + | 前一个字符至少出现1次 | a+b | aaab或ab |
15 | ? | 前一个字符只能出现一次或者不出现 | a?b | ab或b |
16 | {m} | 允许前一个字符只能出现m次 | a{3}b | aaab |
17 | {m,n} | 允许前一个字符至少出现m次,最多出现n次(如果不写n,则代表至少出现m次) | a{3,5}b和a{3,} | aaaab和aaaaaab |
18 | ^ | 匹配字符串的开始,多行内容时匹配每一行的开始 | ^abc | abc |
19 | $ | 匹配字符串的结尾,多行内容时匹配每一行的结尾 | abc& | abc |
20 | \A | 匹配字符串开始位置,忽略多行模式 | \Aabc | abc |
21 | \Z | 匹配字符串结束位置,忽略多行模式 | abc\Z | abc |
22 | \b | 匹配位于单词开始或结束位置的空字符串 | hello \bworld | hello world |
23 | \B | 匹配不位于单词开始或结束位置的空字符串 | he\Bllo | hello |
24 | | | 表示左右表达式任意满足一种即可 | abc|cba | abc或cba |
25 | (…) | 将被括起来的表达式作为一个分组,可以使用索引单独取出 | (abc)d | abcd |
26 | (?P<name>…) | 为该分组起一个名字,可以用索引或名字去除该分组 | (?P<id>abc)d | abcd |
27 | \number | 引用索引为number中的内容 | (abc)d\1 | abcdabc |
28 | (?P=name) | 引用该name分组中的内容 | (?P<id>abc)d(?P=id) | abcdabc |
29 | (?:…) | 分组的不捕获模式,计算索引时会跳过这个分组 | (?:a)b(c)d\1 | abcdc |
30 | (?iLmsux) | 分组中可以设置模式,iLmsux之中的每个字符代表一个模式,单独介绍 | (?i)abc | Abc |
31 | (?#…) | 注释,#后面的内容会被忽略 | ab(?#注释)123 | ab123 |
32 | (?=…) | 顺序肯定环视,表示所在位置右侧能够匹配括号内正则 | a(?=\d) | a1最后的结果得到a |
33 | (?!…) | 顺序否定环视,表示所在位置右侧不能匹配括号内正则 | a(?!\w) | a c最后的结果得到a |
34 | (?<=…) | 逆序肯定环视,表示所在位置左侧能够匹配括号内正则 | 1(?<=\w)a | 1a |
35 | (?<!…) | 逆序否定环视,表示所在位置左侧不能匹配括号内正则 | 1 (?<!\w)a | 1 a |
36 | (?(id/name)yes|no) | 如果前面的索引为id或者名字为name的分组匹配成功则匹配yes区域的表达式,否则匹配no区域的表达式,no可以省略 | (\d)(?(1)\d|a) | 32 |
二、(?iLmsux)为分组设置模式
这里的”i”, “L”, “m”, “s”, “u”, “x”,它们不匹配任何字串,而是表示对应python中re模块当中的(re.I, re.L, re.M, re.S, re.U, re.X)的6种选项。
可以在python源码中看到:
I = IGNORECASE # 忽略大小写
L = LOCALE # 字符集本地化,为了支持多语言版本的字符集使用环境
U = UNICODE # 使用\w,\W,\b,\B这些元字符时将按照UNICODE定义的属性
M = MULTILINE # 多行模式,改变 ^ 和 $ 的行为
S = DOTALL # '.' 的匹配不受限制,包括换行符
X = VERBOSE # 冗余模式,可以忽略正则表达式中的空白和#号的注释
- 1
- 2
- 3
- 4
- 5
- 6
六种模式在正则表达式中可以同时使用多个的,在 python 里面使用按位或运算符 | 同时添加多个模式
如:re.compile(”, re.I|re.M|re.S)
三 、反斜杠的使用
在一般的编程语言当中,反斜杠“\”代表反转义字符,在反斜杠后面加一个字符可以表示一种特定的意思,接下来列举几个常见的转义字符:
序号 | 转义字符 | 解释 |
---|---|---|
1 | \f | 换页,将当前位置移到下页开头 |
2 | \n | 换行,将当前位置移到下一行开头 |
3 | \r | 回车,将当前位置移到本行开头 |
4 | \t | 水平制表,跳到下一个TAB位置 |
5 | \v | 垂直制表(暂时还没用过这个) |
6 | \\ | 代表一个反斜杠字符 ‘\’ |
因为在正则表达式的规则当中,‘\’就是转义字符的意思,前面基本语法规则里也有说到,但是在一般的变成语言中,’\’也有转义字符的意思,所以,如果我们要是写代码的时候用到正则表达式中的’\’的时候,就需要写四个’\’,才可以代表一个真正的反斜杠字符,如:“\\\\”。
“\\\\”:这里的第一个和第三个是在编程语言中起转义的作用,结果:“\\”,然后在正则表达式中它在进行一次反转义就代表着一个真正的反斜杠字符了。
但是在python语言中,我们可以不考虑这个问题。只需要在写好的字符串前面加一个’r’, 告诉编译器这个字符串是个原生字符串,不要转义’\’ 。
例如,上个例子中的正则表达式可以使用r”\”表示。因为加上这个’r’,只是告诉python语言不转义这个字符串,但是在正则表达式中还是要符合正则表达式的规则。同样,匹配一个数字的”\d”就可以写成r”\d”。所以,在python中写正则表达式时,要养成一个前面写’r’的习惯。
四、总结
这些就是正则表达式的基本语法了,和学习编程一样,活学活用,不懂就查。接下来将写一下在python3中使用正则表达式即re模块的学习笔记。
大家可以在写的时候参考一下:最全的常用正则表达式大全
在Python3正则表达式(一)基本语法规则已经记录了正则表达式的基本规则,接下来将写一下在python当中如何利用正则表达式去匹配字符串,即re模块中功能函数的使用。
使用时要先进行导入re模块:import re
一、re模块中常用的函数
1.compile()
源码描述:
def compile(pattern, flags=0):
"Compile a regular expression pattern, returning a pattern object."
# 生成一个正则表达式模式,返回一个Regex对象
return _compile(pattern, flags)
- 1
- 2
- 3
- 4
参数说明:
pattern: 正则表达式
flags: 用于修改正则表达式的匹配方式,就是我们在基本语法规则中说到的(iLmsux)六种模式,默认正常模式
- 1
- 2
示例代码:
pattern = re.compile(r"\d")
result = pattern.match("123")
print(result.group())
# 输出结果为1 因为这里只有一个\d 所以只匹配到一个数字
pattern = re.compile(r"abc d", re.I|re.X)
result = pattern.match("AbcD")
print(result.group())
# 输出结果为AbcD 证明可以同时使用多个模式
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
2.match()
源码描述:
1. 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."""
# 在字符串的开头匹配pattern,返回Match匹配对象,如果没有不到匹配的对象,返回None。
return _compile(pattern, flags).match(string)
2. def match(self, string, pos=0, endpos=-1):
"""Matches zero | more characters at the beginning of the string."""
pass
# 可以指定匹配的字符串起始位置
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
参数说明:
# 其他两个参数与compile()当中的意义一致
string: 需要验证的字符串
pos: 设定开始位置,默认0
endpos: 设定结束位置,默认-1
- 1
- 2
- 3
- 4
示例代码:
result = re.match(r"a+\d", "aA123", re.I)
print(result.group())
# 输出结果为aA1 只要pattern匹配完了,则视为成功,并将匹配成功的字符串返回
pattern = re.compile(r"abc d", re.I|re.X)
result = pattern.match("0AbcD5", 1, 5)
print(result.group())
# 输出结果为AbcD 从第1个位置开始,到第5个位置之前的字符串
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
3.search()
源码描述:
1. def search(pattern, string, flags=0):
"""Scan through string looking for a match to the pattern, returning a match object, or None if no match was found."""
# 大致意思与match方法相同,不同的地方在于search时整个字符串任意位置匹配,而match时从特定的位置(pos)开始向后仅匹配一次
return _compile(pattern, flags).search(string)
2. def search(self, string, pos=0, endpos=-1):
"""Scan through string looking for a match, and return a corresponding match instance. Return None if no position in the string matches."""
pass
# 可以指定字符串的子串进行匹配
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
参数说明:
# 与match中的一致
- 1
示例代码:
pattern = re.compile(r"abc d", re.I|re.X)
result = pattern.search("0A2aBcd7")
print(result.group())
# 输出结果为aBcd 在字符串中任意位置只要匹配到就返回结果
pattern = re.compile(r"abc d", re.I|re.X)
matchResult = pattern.match("0AbcD5")
searchResult = pattern.search("0AbcD5")
# matchResult的结果是None
# searchResult.group()的结果结果为AbcD
# 因为在pattern中第一个位置是a,但是在字符串中第一个位置是0,所以match方法在这里匹配失败
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
4.group(),groups()和groupdict()
源码描述:
1.def group(self, *args):
"""Return one or more subgroups of the match."""
# 返回成功匹配的一个或多个子组
pass
2.def groups(self, default=None):
"""Return a tuple containing all the subgroups of the match, from 1 up to however many groups are in the pattern."""
# 以元组的格式返回所有分组匹配到的字符
pass
3.def groupdict(self, default=None):
"""Return a dictionary containing all the named subgroups of the match,keyed by the subgroup name."""
# 以字典的格式返回所有分组匹配到的字符
pass
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
参数说明:
group中的*args: 如果参数为一个,就返回一个子串;如果参数有多个,就返回多个子串的元组。如果不传任何参数,和传入0一样,将返回整个匹配子串。
groups中的default: 用于给那些没有匹配到的分组做默认值,它的默认值是None
groupdict中的default: 用于给那些没有匹配到的分组做默认值,它的默认值是None
- 1
- 2
- 3
示例代码:
pattern = re.compile(r"([\w]+) ([\w]+)")
m = pattern.match("Hello World Hi Python")
print(m.group())
# 输出结果为Hello World 第一个分组成功匹配到Hello第二个成功匹配到World 正则表达式已匹配结束
print(m.group(1))
# 输出结果为Hello 取第一个分组成功匹配到Hello
print(m.group(2))
# 输出结果为World 取第二个分组成功匹配到World
pattern = re.compile(r"([\w]+)\.?([\w]+)?")
m = pattern.match("Hello")
print(m.groups())
# 输出结果为('Hello', None) 第一个元素是一个分组匹配到的Hello,因为第二个分组没有匹配到,所以返回None
print(m.groups("Python"))
# 输出结果为('Hello', 'Python') 因为第二个分组没有匹配到,所以返回在groups中设置的默认值
pattern = re.compile(r"(?P<first_str>\w+) (?P<last_str>\w+)")
m = pattern.match("Hello Python")
print(m.groupdict())
# 输出结果为{'first_name': 'Hello', 'last_name': 'Python'} 默认值的用法与groups中的相同
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
5.findall()
源码描述:
def findall(self, string, pos=0, endpos=-1):
"""Return a list of all non-overlapping matches of pattern in string."""
# 返回字符串中所有匹配成功的子串的列表,
#重点:返回的是一个列表,没有group方法
pass
- 1
- 2
- 3
- 4
- 5
- 6
参数说明:
# 与match方法一致
- 1
示例代码:
pattern = re.compile(r'\d+')
m = pattern.findall('a1b2c33d4')
print(m)
# 输出['1', '2', '33', '4'] 查找出字符串中的所有数字
m = pattern.findall('a1b2c33d4', 1, 6)
print(m)
# 输出['1', '2', '3'] 在"1b2c3"中查找
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
6.finditer()
源码描述:
def finditer(self, string, pos=0, endpos=-1):
"""Return an iterator over all non-overlapping matches for the pattern in string. For each match, the iterator returns a match object."""
# 返回字符串中所有匹配成功的子串的迭代器
pass
- 1
- 2
- 3
- 4
- 5
参数说明:
# 与match方法一致
- 1
示例代码:
pattern = re.compile(r'\d+')
m = pattern.finditer('a1b2c33d4')
print(m)
# 输出<callable_iterator object at 0x0000017A8C0F8240>迭代器
print(next(m).group())
# 依次输出匹配到的结果
- 1
- 2
- 3
- 4
- 5
- 6
- 7
7.finditer()
源码描述:
def split(self, string, maxsplit=0):
"""Split string by the occurrences of pattern."""
# 返回根据匹配到的的子串将字符串分割后成列表
pass
- 1
- 2
- 3
- 4
- 5
参数说明:
maxsplit: 指定最大分割次数,不指定将全部分割。
- 1
示例代码:
pattern = re.compile(r'\d+')
m = pattern.split('a1b2c3d4e')
print(m)
# 输出['a', 'b', 'c', 'd', 'e'] 根据数字,全部分割
m = pattern.split('a1b2c3d4e', 3)
print(m)
# 输出['a', 'b', 'c', 'd4e'] 只分割三次,后面的不进行分割
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
8.split()
源码描述:
def split(self, string, maxsplit=0):
"""Split string by the occurrences of pattern."""
# 返回根据匹配到的的子串将字符串分割后成列表
pass
- 1
- 2
- 3
- 4
- 5
参数说明:
maxsplit: 指定最大分割次数,不指定将全部分割。
- 1
示例代码:
pattern = re.compile(r'\d+')
m = pattern.split('a1b2c3d4e')
print(m)
# 输出['a', 'b', 'c', 'd', 'e'] 根据数字,全部分割
m = pattern.split('a1b2c3d4e', 3)
print(m)
# 输出['a', 'b', 'c', 'd4e'] 只分割三次,后面的不进行分割
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
9.sub()
源码描述:
def sub(self, repl, string, count=0):
"""Return the string obtained by replacing the leftmost non-overlapping occurrences of pattern in string by the replacement repl."""
# repl替换掉字符串中匹配到的子串,变成新的字符串返回
pass
- 1
- 2
- 3
- 4
- 5
参数说明:
repl: 替补内容
string: 原字符串
count: 替换次数,默认全部替换
- 1
- 2
- 3
示例代码:
pattern = re.compile(r'\s+')
text = "Process finished with exit code 0"
m = pattern.sub('-', text, 3)
print(m)
# 输出结果Process-finished-with-exit code 0 前三个空格被‘-’替换了
- 1
- 2
- 3
- 4
- 5
10.subn()
源码描述:
def subn(self, repl, string, count=0):
"""Return the tuple (new_string, number_of_subs_made) found by replacing the leftmost non-overlapping occurrences of pattern with the replacement repl."""
# 返回一个由替换后的结果和替换的次数组成的元组
pass
- 1
- 2
- 3
- 4
- 5
参数说明:
与sub()参数含义一致
- 1
示例代码:
pattern = re.compile(r'\s+')
text = "Process finished with exit code 0"
m = pattern.subn('-', text)
print(m)
# 输出结果('Process-finished-with-exit-code-0', 5)
- 1
- 2
- 3
- 4
- 5
二、总结
上一部分只是记录了re模块当中比较常用的十种方法,大家可以在源码中看到另外几种简单的或者不常用的方法:
fullmatch(string, pos=0, endpos=-1)
start(group=0)
end(group=0)
escape(string)
如果可以掌握上述的十种方法,那理解这四种方法也是轻而易举。
re模块的使用方法就讲这么多了,如果有错误的地方,希望可以指正,我自己也是在学习阶段,谢谢。
介绍一个正则测试小工具:正则表达式测试工具
后续,还将在写一篇 Python3正则表达式(三)贪婪模式与非贪婪模式
<div id="article_content" class="article_content csdn-tracking-statistics tracking-click" data-mod="popu_519" data-dsm="post">
<div class="markdown_views">
<p>在前面两篇文章当中已经介绍了python用正则表达式的基本运用,接下来总结一下正则表达式中的贪婪模式和非贪婪模式。</p>
一、概念
首先举个例子:
example = "abbbbbbc"
pattern = re.compile("ab+")
贪婪模式:正则表达式一般趋向于最大长度匹配,也就是所谓的贪婪匹配。如上面使用模式pattern 匹配字符串example,匹配到的结果就是”abbbbbb”整个字符串。
非贪婪模式:在整个表达式匹配成功的前提下,尽可能少的匹配。如上面使用模式pattern 匹配字符串example,匹配到的结果就只是”ab”整个字符串。
二、使用方法
在python中默认采用的是贪婪模式,使用非贪婪模式的话,只需要在量词后面直接加上一个问号”?”。
在第一篇文章中介绍了正则表达式当中的量词一共有五种:
三、原理分析
在正则表达式中一般默认采用的是贪婪模式,在上面的例子当中已经匹配到了“ab”时已经可以使整个表达式匹配成功,但是由于采用的是贪婪模式,所以还需要往后继续匹配,检查时候存在更长的可以匹配成功的字符串。一直到匹配到最后一个”b”的时候,后面已经没有可以成功匹配的字符串了,匹配结束。返回匹配结果“abbbbbb”。
所以,我们可以将贪婪模式理解为:在整个表达式匹配成功的前提下,尽可能多的匹配。
非贪婪模式也就是将我们例子中的正则表达式“ab+”改为”ab+?”,当匹配到“ab”时,已经匹配成功,直接结束匹配,不在向后继续尝试,返回匹配成功的字符串”ab”。
所以,我们可以将非贪婪模式理解为:在整个表达式匹配成功的前提下,尽可能少的匹配
四、实例
import re
example = "<div>test1</div><div>test2</div>"
greedPattern = re.compile("<div>.*</div>")
notGreedPattern = re.compile("<div>.*?</div>")
greedResult = greedPattern.search(example)
notGreedResult = notGreedPattern.search(example)
print("greedResult = %s" % greedResult.group())
print("notGreedResult = %s" % notGreedResult.group())
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
运行结果:
五、总结
1.从应用角度看贪婪与非贪婪
贪婪与非贪婪模式影响的是被量词修饰的子表达式的匹配行为,贪婪模式在整个表达式匹配成功的前提下,尽可能多的匹配;而非贪婪模式在整个表达式匹配成功的前提下,尽可能少的匹配。
2.从匹配原理角度看贪婪与非贪婪
能达到同样匹配结果的贪婪与非贪婪模式,通常是贪婪模式的匹配效率较高。 所有的非贪婪模式,都可以通过修改量词修饰的子表达式,转换为贪婪模式。 贪婪模式可以与固化分组结合,提升匹配效率,而非贪婪模式却不可以。