Python re模块 正则表达式

正则表达式在很多语言里都支持,Python也不例外,re模块就是正则表达式的应用。

正则表达式对字符串的逻辑操作,主要是对字符串的一种过滤,用“元字符” 与“普通字符”组成一个字符串规则对已知的字符串或文本过滤出自己想要的字符串。

1、正则表达式元字符

\

转义字符,将后边紧跟着的字符变成特殊字符,或将后边的特殊字符变成普通字符

如:在正则表达式中,"\n"换行符,"\\"则代表一个普通字符"\"

^匹配第一行行首,匹配多行需要传参 flags=re.MULTILINE
$匹配最后一行行尾,匹配多行需要传参 flags=re.MULTILINE
.除"\r""\n"外,匹配任意的单个字符,要使"."匹配换行符,flags=re.DOTALL
|或,如 "aaa|bbb|ccc",表示"aaa","bbb","ccc"三选一
?匹配前边的子表达式0次或1次,等价于{0,1}
+匹配前边的子表达式1次或多次,等价于{1,}
*匹配前边的子表达式0次或多次,,等价于{0,}
{}

{n}匹配前边的子表达式n次,{n,}匹配前边的子表达式至少n次 即:>= n,

{n,m}匹配前边的子表达式n~m次,即:n<= 表达式 <=m

()分组,从1开始,从左至右数"("为第几组,下标0为全部分组
[]

字符集匹配[]中的人一个字符,之匹配一次,如[abc]:表示"a","b","c"三选一。

也可以给定范围(必须是连续的才行),如[a-z]:表示a到z任意一个字符。

还可以取反,如[^abc]:除"a","b","c"外的任意字符。

注:[]中只有"^","-","\"三个特殊字符,其中"\"代表转义字符,

其它的都代表原本普通的字符,如:[.]只是一个普通的点字符

注:要使用元字符本身的普通字符,请使用转义字符转义一下,如 :"\(" 在正则表达式中代表普通给"("字符,其它的雷同

分组的用法:

(\num)引用指定第几分组的值
(?P<name>)指定分组别名"name"
(?P=name)引用指定别名的分组的值
(?:)

相当于去分组化,变成"与"()子集,

如:"abc(?:qqq|www)"-->相当于"abcqqq|abcwww"

(具体用法请看下边(search函数))

2、预定义字符集

\b

匹配一个单词边界,也就是指单词和空格间的位置,

其它特殊字符也可以是单词的边界,如"#","$","&","*"等

\B匹配非单词边界
\d匹配一个数字字符。等价于[0-9]
\D匹配一个非数字字符。等价于[^0-9
\s

匹配任何不可见字符,包括空格、制表符、换页符等,

等价于[ \f\n\r\t\v]

\S匹配任何可见字符。等价于[^ \f\n\r\t\v]
\w

匹配包括下划线的任何单词字符。这里的"单词"字符使用Unicode字符集,

类似但不等价于“[A-Za-z0-9_]”,还包含汉字等它国字符

\W

匹配任何非单词字符。这里的"单词"字符使用Unicode字符集,

类似但不等价于“[^A-Za-z0-9_]”,还包含汉字等它国字符

3、re模块常用函数

在这里我要先说明一下 flags 的用法

re.A 或 re.ASCII使用ASCII字符集进行匹配(不常用)
re.I 或 re.IGNORECASE忽略大小写匹配
re.L 或 re.LOCALE

使用当前预定字符类 \w \W \b \B \s \S

取决于当前区域设定(不常用)

re.U 或 re.UNICODE

使用Unicode字符类 \w \W \b \B \s \S \d \D

取决于unicode定义的字符属性(不常用)

re.M 或 re.MULTILINE多行匹配,使"^","$"可以在每一行中都进行匹配
re.S 或 re.DOTALL使 "." 可以匹配换行符"\r","\n"
re.X 或 re.VERBOSE去掉正则表达式中的所有空格符(不常用)

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

按照规则匹配整个字符串,返回匹配结果的列表

>>> re.findall(r"hello", "hello world hello")  # 普通匹配
['hello', 'hello']
>>> re.findall(r"^hello", "hello world hello")  # 匹配开头
['hello']
>>> re.findall(r"^hello", "hello world\nhello", flags=re.MULTILINE)  # 多行匹配开头
['hello', 'hello']
>>> re.findall(r"hello$", "hello world hello")  # 匹配结尾
['hello']
>>> re.findall(r"\d+", "aaa111bbb222ccc333")  # 匹配数字
['111', '222', '333']
>>> re.findall(r"\d{2}", "aaa111bbb222ccc333")  # 匹配两位的数字
['11', '22', '33']
>>> re.findall(r"ab|cd", "ab000cd00")  # 匹配"ab"或"cd"
['ab', 'cd']
>>> re.findall(r"\(", "ab(cd"))  # 匹配"("
['(']

>>> re.findall(r"(abc)+", "abcabcabc")  # 想要匹配多个"abc",使用分组时会优先把分组的内容返回
['abc']
>>> re.findall(r"(?:abc)+", "abcabcabc")  # 想要匹配多个"abc",(?:)把分组去掉,变成一个普通的字符串
['abcabcabc']

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

finditer与findall相似,只不过finditer返回一个迭代器,迭代器中每一个元素都是re.Match对象,通过group()可以获取值

r = re.finditer("hello", "hello world hello")
for i in r:
    print(i.group())

# 输出结果
hello
hello

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

search函数只返回第一个匹配到的结果,成功返回re.Match对象,否则返回None。

>>> re.search(r"hello", "hello world hello")
<_sre.SRE_Match object; span=(0, 5), match='hello'>  # 可以看到只返回了第一个"hello"
>>> re.search(r"hello", "hello world hello").group()
'hello'

>>> re.search(r"(abc) \1", "abc abc").group()  # \1 引用分组1的值
'abc abc'
>>> re.search(r"(?P<name>abc) (?P=name)", "abc abc").group()  # (?P=name) 引用分组别名"name"的值
'abc abc'

>>> re.search(r"(?P<name>zhansan) (?P<age>23)", "zhansan 23").group()
'zhansan 23'
>>> re.search(r"(?P<name>zhansan) (?P<age>23)", "zhansan 23").group(0)  # 分组0--即匹配的结果
'zhansan 23'
>>> re.search(r"(?P<name>zhansan) (?P<age>23)", "zhansan 23").group(1)  # 分组1的值
'zhansan'
>>> re.search(r"(?P<name>zhansan) (?P<age>23)", "zhansan 23").group(2)  # 分组2的值
'23'
>>> re.search(r"(?P<name>zhansan) (?P<age>23)", "zhansan 23").group("name")  # 分组别名name的值
'zhansan'
>>> re.search(r"(?P<name>zhansan) (?P<age>23)", "zhansan 23").group("age")  # 分组别名age的值
'23'

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

match从开始处进行匹配,成功返回re.Match对象,否则返回None。类似于于findall代码中的第3行

1 >>> re.findall(r"^abc", "abcooooo")
2 ['abc']
3 >>> re.match(r"abc", "abcooooo").group()
4 'abc'

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

sub按照给定的规则将string字符串中的相应的片段替换为repl,count 最多替换的次数,count=0默认为全部替换,返回替换后的字符串

1 >>> re.sub(r"\d+", "$", "aaa1bb2ccc333")  # 将连续的数字变成"$"
2 'aaa$bb$ccc$'

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

与sub相识,count 最多替换的次数,count=0默认为全部替换,返回 一个元组,下标0为替换后的字符串,下标1成功替换的次数

>>> re.subn(r"\d+", "$", "aaabbccc")
('aaabbccc', 0)
>>> re.subn(r"\d+", "$", "aaa11bb22ccc")
('aaa$bb$ccc', 2)
>>> re.subn(r"\d+", "$", "aaa11bb22ccc", 1)  # 最多替换1次
('aaa$bb22ccc', 1)
>>> re.subn(r"\d+", "$", "aaa11bb22ccc", 10)  # 最多替换10次
('aaa$bb$ccc', 2)  # 成功替换2次

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

通过给定规则,将string进行切割,maxsplit最多切割次数,maxsplit=0默认全部切割,返回切割结果的字符串列表

1 >>> re.split(r"@", "a#b#c#d#e")  # 没有找到"@",不切割
2 ['a#b#c#d#e']
3 >>> re.split(r"#", "a#b#c#d#e")  # 按"#"对字符串进行切割
4 ['a', 'b', 'c', 'd', 'e']
5 >>> re.split(r"#", "a#b#c#d#e", 2)  # 按"#"对字符串进行切割,最多切割2次
6 ['a', 'b', 'c#d#e']

8)re.compile(pattern, flags=0)

返回一个正则表达式的对象

1 >>> rg = re.compile(r"\d+", flags=0)
2 >>> re.findall(rg, "a1bb22ccc333")
3 ['1', '22', '333']

4、注意事项

1)"r" 原生字符串的使用,在字符串前加 "r" 或 "R" 表示这个字符串为原生字符串,即所有的字符都表示普通字符,无特殊字符,常在正则、路径中应用

>>> print(r"aa\nbb")
aa\nbb
>>> print(len(r"aa\nbb"))
6
>>> print("aa\nbb")
aa
bb
>>> print(len("aa\nbb"))
5

可以看到第四行,字符串长度为"6" ,即 "\" "n" 是两个字符。

第6、7行输出了两行,第9行字符串长度"5",说明 "\n"  是一个换行符

2)例子:如果需要匹配 "\" 该如何写正则表达式(重点)

>>> re.findall(r"aa\\", r"aa\bb")
['aa\\']
>>> re.findall("aa\\", r"aa\bb")
Traceback (most recent call last):
  # 报错内容····太多,就这样显示就好
>>> re.findall("aa\\\\", r"aa\bb")
['aa\\']
>>> print(len(re.findall(r"aa\\", r"aa\bb")[0]))
3

在这里首先要说明一下,在python中,输出带引号的字符串中有 "\" 字符,是转义后看到的字符,即 "\\"

可以看到,在匹配带转义字符的正则表达式的中带 "r" 和不带 "r" 的写法有很大的区别

其中的原因是python解释器在解释字符串的时候会按自己的规则将字符串翻译了一便。

如第3行,"aa\\" 经过python 解释器翻译之后,已经变成了  r"aa\" 这样的原生字符串,传递给re模块的时候,re模块无法解析单个 "\" 转义字符,所以会报错。

如果要匹配  "\"  的普通字符,正则表达式需要写出 "\\" ,所以第6行传递的时候需要 4 个"\\\\" ,经过python解释之后,变成 r"\\" 就正确了。

综上所述,在写正则表达式的时候还是在前边加个 "r" 这样或许会避免很多的问题(个人建议,特别是在网上找的正则表达式)

相关推荐