Python Regexp 函数方法

3. re 模块操作

  1. re 模块的使用过程

4. 匹配单个字符

正则表达式的单字符匹配

字符 功能
. 匹配任意 1 个字符(除了)
[ ] 匹配[ ]中列举的字符
匹配数字,即 0-9
匹配非数字,即不是数字
匹配空白,即空格,tab 键
匹配非空白
匹配单词字符,即 az、AZ、0-9、_
匹配非单词字符
  • . 匹配任意一个字符

配单个数字

[12345678] 匹配 1-8 中任意一个数字

[138] 匹配 1、3、8 中任意一个数字

[1-8] 同上

[1-36-8] s 匹配 1-3 或 6-8

1
2
3
4
5
6
7
8
In [17]: re.match(r'速度与激情[12345678]','速度与激情4')
Out[17]: <re.Match object; span=(0, 6), match='速度与激情4'>

In [25]: re.match(r'速度与激情[1-8]','速度与激情35')
Out[25]: <re.Match object; span=(0, 6), match='速度与激情3'>

In [31]: re.match(r'速度与激情[1-36-8]','速度与激情7')
Out[31]: <re.Match object; span=(0, 6), match='速度与激情7'>

[1-8abcd] 匹配数字 1-8 以及 a、b、c、d 四个字符

[1-8a-d] 匹配数字 1-8 以及 a-d

[1-8a-zA-Z] 匹配数字 1-8 以及 a-z 以及 A-Z

1
2
3
4
5
6
7
8
9
10
11
In [33]: re.match(r'速度与激情[1-8abcd]','速度与激情7')
Out[33]: <re.Match object; span=(0, 6), match='速度与激情7'>

In [38]: re.match(r'速度与激情[1-8a-d]','速度与激情a')
Out[38]: <re.Match object; span=(0, 6), match='速度与激情a'>

In [39]: re.match(r'速度与激情[1-8a-zA-Z]','速度与激情f')
Out[39]: <re.Match object; span=(0, 6), match='速度与激情f'>

In [40]: re.match(r'速度与激情[1-8a-zA-Z]','速度与激情K')
Out[40]: <re.Match object; span=(0, 6), match='速度与激情K'>

配一个数字

匹配一个 word,其中包括:a-z, A-Z,0-9 还有下划线(_), 此外,它还能匹配中文。所以一般不使用它,使用[a-zA-Z0-9_] 来代替它

匹配一个空白字符,如:空格、 等

所有的大写字母都是匹配与小写相反的字符。

1
2
3
4
5
6
7
8
In [43]: re.match(r'速度与激情\w','速度与激情5')
Out[43]: <re.Match object; span=(0, 6), match='速度与激情5'>

In [44]: re.match(r'速度与激情\w','速度与激情_')
Out[44]: <re.Match object; span=(0, 6), match='速度与激情_'>

In [41]: re.match(r'速度与激情\w','速度与激情K')
Out[41]: <re.Match object; span=(0, 6), match='速度与激情K'>

5. 匹配多个字符

字符 功能
* 匹配前一个字符出现 0 次或者无限次,即可有可无
+ 匹配前一个字符出现 1 次或者无限次,即至少有 1 次
? 匹配前一个字符出现 1 次或者 0 次,即要么有 1 次,要么没有
{m} 匹配前一个字符出现 m 次
{m,n} 匹配前一个字符出现从 m 到 n 次

最简单的思路:多写几个单个字符匹配就成了多个字符匹配。

缺点:只能匹配指定个数的字符,多了少了都会报错。

1
2
3
4
5
6
7
8
In [12]: re.match(r'速度与激情\d\d', '速度与激情45').group()
Out[12]: '速度与激情45'

In [13]: re.match(r'速度与激情\d\d', '速度与激情4').group()
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-13-bc99d57c6a2a> in <module>
----> 1 re.match(r'速度与激情\d\d', '速度与激情4').group()

使用大括号来限制前面的单个字符有多少个。

匹配 1 到 8 位数字,至少匹配 1 个数字,至多匹配 4 个数字

匹配 11 个连续的数字,中间不能有别的字符。

g{1,3} 匹配至少 1 个,至多 2 个字符 g

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
In [15]: re.match(r'速度与激情\d{1,2}', '速度与激情3').group()
Out[15]: '速度与激情3'

In [16]: re.match(r'速度与激情\d{1,2}', '速度与激情45').group()
Out[16]: '速度与激情45'

In [17]: re.match(r'速度与激情\d{4}', '速度与激情4568').group()
Out[17]: '速度与激情4568'

In [18]: re.match(r'速度与激情\d{4}', '速度与激情45a68').group()
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-18-c9758e381471> in <module>
----> 1 re.match(r'速度与激情\d{4}', '速度与激情45a68').group()

In [27]: re.match(r'速度与激情a{4}', '速度与激情aaaa').group()
Out[27]: '速度与激情aaaa'

In [28]: re.match(r'速度与激情a{1,4}', '速度与激情aaaa').group()
Out[28]: '速度与激情aaaa'

In [29]: re.match(r'速度与激情a{1,4}', '速度与激情aaa').group()
Out[29]: '速度与激情aaa'

? 前面的字符出现一次或零次

1
2
3
4
5
In [33]: re.match(r'021-?\d{8}', '021-12345678').group()
Out[33]: '021-12345678'

In [34]: re.match(r'021-?\d{8}', '02112345678').group()
Out[34]: '02112345678'

注意:三个双引号是字符串,放在函数下面,可以当作函数的说明。放到类的下面,可以看作是类的说明。

要想匹配所有字符,包括 ,可以使用

1
In [52]: re.match(r'.*',html_content,re.S).group()

单独使用 .* 只能匹配单行所有字符(有无),一旦遇到换行符就停止匹配了。可以加一个参数 re.S

+ 与 * 的区别: 前者不能匹配 空字符,后者可以匹配空字符

6. 位置锚定

字符 功能
^ 匹配字符串开头
$ 匹配字符串结尾

配一个单词边界,也就是指单词和空格间的位置。 例如,“er可以匹配“never”中的“er”,但不能匹配“verb”中的“er”。 匹配非单词边界。“ 例如:er”能匹配“verb”中的“er”,但不能匹配“never”中的“er”。

为什么不写^ 也能匹配开头,因为 python 的 match 函数**自动**从开头开始匹配,但是不会默认自动匹配到结尾。但是其他语言的函数不一定这样做。所以如果想要匹配整个字符串的还,一般开头写上 ^, 结尾写上 $

比如:判断变量名是否有效 问题中,以下代码就不能判断类似:‘a#ss’ 等在中间出现不正常字符的变量名

1
ret = re.match(r'[a-zA-Z_][\w]*', name)

改成:

1
ret = re.match(r'^[a-zA-Z_][\w]*$', name)

练习:匹配 4-13 个前缀(字母数字下划线)并以@163.com 结尾的邮箱,正则为:

1
ret = re.match(r'^[a-zA-Z0-9_]{4,20}@163\.com$', mail)

⚠️ 注意:

  1. 如果要全字符串匹配,不要忘记在最后使用 $ 符号
  2. 如果要在字符串中匹配那些正则表达式用到的字符,需要使用反斜杠进行转义。

7. 匹配分组

字符 功能
| 匹配左右任意一个表达式
(ab) 将括号中字符作为一个分组
\num 引用分组 num 匹配到的字符串
(?P) 分组起别名
(?P=name) 引用别名为 name 分组匹配到的字符串

小括号有两个功能:

  1. 分组匹配

  2. 单独取出小括号中的值

    想要取某个位置的值,就给它加个括号。想要取第 n 个括号里面的东西,就用 group(n).

    如果括号的个数为 m,你用 group(m+1) 会引发异常。

注意:测试过程中为了简洁起见,有时会使用 re.match().group() 这样的写法。正常开发代码的时候,需要分开写。因为前者会产生异常。

对上次写的匹配邮箱的正则进行改进,匹配多种邮箱:

1
ret = re.match(r'^[a-zA-Z0-9_]{4,20}@(qq|outlook|163|gmail|126)\.com$', mail)

注意:如果不写括号,那程序会默认将 | 前后的两大部分看成两个分组,而这并不是我们想要的。

分组的另一个应用:

判断 html_str = "<h1>hello wrold</h1>" 中开闭标签是否配对

1
re.match(r'<(\w*)>.*</\1>',html_str)

因为想取某个位置的值,所以给他加上分组 (), 然后后面使用的时候,看他是第几个分组,就用 \几

1
2
3
4
In [9]:  html_str='<body><h1>hello</h1></body>'

In [10]: re.match(r'<(\w*)><(\w*)>.*</\2></\1>',html_str)
Out[10]: <re.Match object; span=(0, 27), match='<body><h1>hello</h1></body>'>

或者当分组过多时,数数并不方面,可以使用别名:

1
2
In [17]: re.match(r'<(?P<p1>\w*)><(?P<p2>\w*)>.*</(?P=p2)></(?P=p1)>',html_str)
Out[17]: <re.Match object; span=(0, 27), match='<body><h1>hello</h1></body>'>

8. re 模块的高级用法

上面的大部分正则知识,在其他语言中基本都有。而这一节 ‘re 模块的高级用法’是 python 特有的,其他的语言可能没有。

match() 是默认要求从头开始匹配,而 search 不是,它只是匹配字串中第一次出现符合正则匹配项的内容。

1
2
In [28]: re.search(r"\d+","阅读次数:7887, 点赞数:454").group()
Out[28]: '7887'

当然,在 search 的正则中加上 ^,就和 match 一样了。

8.2. findall()

search 是只匹配符合正则的第一个字串,而 findall 是返回所有匹配正则的数据,返回值为列表。

一般在网页批量获取特定格式的数据时,经常使用它

⚠️:获取 findall 的返回结果不需要使用 group()

1
2
In [73]: re.findall(r'\d+','python = 999, c = 22,c++ = 1212')
Out[73]: ['999', '22', '1212']

8.3. sub()

  1. 将正则表达式匹配到的数据,替换为你输入的参数值
  2. 支持函数调用

匹配替换:

1
2
In [74]: re.sub(r'\d+','hello','python = 999, c = 22,c++ = 1212')
Out[74]: 'python = hello, c = hello,c++ = hello'

函数调用:

匹配到符合条件的字串时,自动调用函数。将正则匹配的返回对象传到函数中。并接受该函数的返回值来替换匹配到的东西。

8.4. split()

根据匹配进行切割字符串,并返回一个列表。

练习:将冒号或空格作为分隔符,分割字符串:“info:xiaoZhang 33 shandong”

1
2
import re
re。

https://www.jianshu.com/p/9860f951616e

look ahead look behind

常常用于计数: Find a string

1
2
3
4
5
6
7
8
9
10
11
12
import re

def main():
original_str = input()
substring = input()
a, b = substring[:-1], substring[-1]
pattern = rf"{a}(?={b})"
matches = re.findall(pattern, original_str)
print(len(matches))

if __name__ == "__main__":
main()

当然,此题目更适合用简单遍历匹配解决:

1
2
3
4
5
6
7
8
9
def count_substring(string, sub_string):
count = 0
for i in range(0,len(string)):
if (string[i:i+len(sub_string)] == sub_string):
count += 1
print(count)
s=input()
sub=input()
count_substring(s, sub)