Lipsky

Regular Expression

字数统计: 1.9k阅读时长: 9 min
2018/08/17 Share

正则表达式(Regular Expression)#

正则是一种用来匹配字符串的的工具🔧,简称RegEx,在大量的字符串中寻找出所需要的字符。e.g. 在网络爬虫中寻找出需要的关键字、关键内容;在进行数据筛选时使用。

简单匹配#

简单的匹配类型规则:#

  • \d 可以匹配一个数字,\w 可以匹配一个字母或者数字:

    • ‘00\d’ 可以匹配 ‘007’ 但无法匹配 ‘00A’;

    • ‘\d\d\d’ 可以匹配’010’;

    • ‘\w\w\d’ 可以匹配 ‘py3’;

  • . 是可以匹配任意字符的,有:

    • ‘py.’ 可以匹配 ‘pyc’; ‘pya’; ‘pyb’ ……
  • 要匹配的长度在正则表达式中,可以用 * 来表示人一个字符(包括0个),用 + 来表示至少一个字符,用 ?来表示0个或1个字符,用 {n} 来表示 n 个字符,用 {n,m} 来表示 n-m 个字符:

    e.g.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # * : occur 0 or more times
    print(re.search(r"ab*", "a")) # <_sre.SRE_Match object; span=(0, 1), match='a'>
    print(re.search(r"ab*", "abbbbb")) # <_sre.SRE_Match object; span=(0, 6), match='abbbbb'>

    # + : occur 1 or more times
    print(re.search(r"ab+", "a")) # None
    print(re.search(r"ab+", "abbbbb")) # <_sre.SRE_Match object; span=(0, 6), match='abbbbb'>

    # {n, m} : occur n to m times
    print(re.search(r"ab{2,10}", "a")) # None
    print(re.search(r"ab{2,10}", "abbbbb")) # <_sre.SRE_Match object; span=(0, 6), match='abbbbb'>
    • \d{3} 表示匹配 3 个数字, e.g. ‘111’

    • \s 匹配一个空格(也可以包括 Tab 等空白符),所以 \s+ 表示至少匹配一个空白符,e.g. 匹配 ‘ ‘,‘ ‘等;

    • \d{3,8} 表示匹配3-8个数字,e.g. ‘1234567‘。

      如果要匹配 ‘010-12345‘ 这样的号码:由于 ‘-‘ 是特殊字符,所以在正则表达式中要用 ‘\’ 来进行转义,所以正确的匹配应该是:\d{3}-\d{3,8}

小总结:

  • \d : 任何数字

  • \D : 不是数字

  • \s : 任何 white space, 如 \t\n\r\f\v

  • \S : 不是 white space

  • \w : 任何大小写字母, 数字和 “” a-zA-Z0-9

  • \W : 不是 \w

  • \b : 空白字符 (在某个字的开头或结尾)

  • \B : 空白字符 (在某个字的开头或结尾)

  • \ : 匹配 \

  • . : 匹配任何字符 (除了 \n)

  • ^ : 匹配开头

  • $ : 匹配结尾

  • ? : 前面的字符可有可无

按类型匹配示例#

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# \d : decimal digit
print(re.search(r"r\dn", "run r4n")) # <_sre.SRE_Match object; span=(4, 7), match='r4n'>

# \D : any non-decimal digit
print(re.search(r"r\Dn", "run r4n")) # <_sre.SRE_Match object; span=(0, 3), match='run'>

# \s : any white space [\t\n\r\f\v]
print(re.search(r"r\sn", "r\nn r4n")) # <_sre.SRE_Match object; span=(0, 3), match='r\nn'>

# \S : opposite to \s, any non-white space
print(re.search(r"r\Sn", "r\nn r4n")) # <_sre.SRE_Match object; span=(4, 7), match='r4n'>

# \w : [a-zA-Z0-9_]
print(re.search(r"r\wn", "r\nn r4n")) # <_sre.SRE_Match object; span=(4, 7), match='r4n'>

# \W : opposite to \w
print(re.search(r"r\Wn", "r\nn r4n")) # <_sre.SRE_Match object; span=(0, 3), match='r\nn'>

# \b : empty string (only at the start or end of the word)
print(re.search(r"\bruns\b", "dog runs to cat")) # <_sre.SRE_Match object; span=(4, 8), match='runs'>

# \B : empty string (but not at the start or end of a word)
print(re.search(r"\B runs \B", "dog runs to cat")) # <_sre.SRE_Match object; span=(8, 14), match=' runs '>

# \ : match \
print(re.search(r"runs\", "runs\ to me")) # <_sre.SRE_Match object; span=(0, 5), match='runs\'>

# . : match anything (except \n)
print(re.search(r"r.n", "r[ns to me")) # <_sre.SRE_Match object; span=(0, 3), match='r[n'>

# ^ : match line beginning
print(re.search(r"^dog", "dog runs to cat")) # <_sre.SRE_Match object; span=(0, 3), match='dog'>

# $ : match line ending
print(re.search(r"cat$", "dog runs to cat")) # <_sre.SRE_Match object; span=(12, 15), match='cat'>

# ? : may or may not occur
print(re.search(r"Mon(day)?", "Monday")) # <_sre.SRE_Match object; span=(0, 6), match='Monday'>
print(re.search(r"Mon(day)?", "Mon")) # <_sre.SRE_Match object; span=(0, 3), match='Mon'>

如果一个字符串有很多行, 我们想使用^形式来匹配行开头的字符, 如果用通常的形式是不成功的. 比如下面的 “I” 出现在第二行开头, 但是使用r"^I"却匹配不到第二行, 这时候, 我们要使用 另外一个参数, 让re.search()可以对每一行单独处理. 这个参数就是flags=re.M, 或者这样写也行flags=re.MULTILINE.

1
2
3
4
5
6
string = """
dog runs to cat.
I run to dog.
"""
print(re.search(r"^I", string)) # None
print(re.search(r"^I", string, flags=re.M)) # <_sre.SRE_Match object; span=(18, 19), match='I'>

使用正则表达式,需要调用python中的一个内置模块 “re”.

  • re.search( ) 的使用:re.search 是 re 模块中一个功能

功能介绍:

1
2
3
4
5
6
import re
a = "cat"
b = "bird"
stirng = "dog runs to cat"
print(re.search(a,string)) #<_sre.SRE_Match object; span=(12, 15), match='cat'>
print(re.search(b,string)) #None

以上,re.search(a,string) : 就是在string 中进行配置寻找 a 的内容,如果匹配到,则返回 Match 到的对象。如果没有匹配得到,则返回 None

灵活匹配方法#

e.g.:

多个元素进行匹配:

1
2
3
# multiple patterns ("run" or "ran")
ptn = r"r[au]n" # start with "r" means raw string
print(re.search(ptn, "dog runs to cat")) # <_sre.SRE_Match object; span=(4, 7), match='run'>

组合匹配:

1
2
3
4
print(re.search(r"r[A-Z]n", "dog runs to cat"))     # None
print(re.search(r"r[a-z]n", "dog runs to cat")) # <_sre.SRE_Match object; span=(4, 7), match='run'>
print(re.search(r"r[0-9]n", "dog r2ns to cat")) # <_sre.SRE_Match object; span=(4, 7), match='r2n'>
print(re.search(r"r[0-9a-z]n", "dog runs to cat")) # <_sre.SRE_Match object; span=(4, 7), match='run'>

分组#

1
2
3
4
5
6
7
8
9
10
11
12
^(\d{3})-(\d{3,8})$分别定义了两个组,可以直接从匹配的字符串中提 取出区号和本地号码:
>>> m = re.match(r'^(\d{3})\-(\d{3,8})$', '010-12345')
>>> m
<_sre.SRE_Match object; span=(0, 9), match='010-12345'>
>>> m.group(0)
'010-12345'
>>> m.group(1)
'010'
>>> m.group(2)
'12345'
如果正则表达式中定义了组,就可以在 Match 对象上用 group()方法提取 出子串来。
注意到 group(0)永远是原始字符串,group(1)、group(2)......表示第 12、......个子串。

findall#

1
2
3
4
5
# findall
print(re.findall(r"r[ua]n", "run ran ren")) # ['run', 'ran']

# | : or
print(re.findall(r"(run|ran)", "run ran ren")) # ['run', 'ran']

replace#

我们还能通过正则表达式匹配上一些形式的字符串然后再替代掉这些字符串. 使用这种匹配re.sub(), 将会比 python 自带的string.replace()要灵活多变.

1
print(re.sub(r"r[au]ns", "catches", "dog runs to cat"))     # dog catches to cat

split#

再来我们 Python 中有个字符串的分割功能, 比如想获取一句话中所有的单词. 比如"a is b".split(" "), 这样它就会产生一个列表来保存所有单词. 但是在正则中, 这种普通的分割也可以做的淋漓精致.

1
print(re.split(r"[,;\.]", "a;b,c.d;e"))             # ['a', 'b', 'c', 'd', 'e']

compile#

最后, 我们还能使用 compile 过后的正则, 来对这个正则重复使用. 先将正则 compile 进一个变量, 比如compiled_re, 然后直接使用这个compiled_re来搜索.

1
2
compiled_re = re.compile(r"r[ua]n")
print(compiled_re.search("dog ran to cat")) # <_sre.SRE_Match object; span=(4, 7), match='ran'>

贪婪匹配#

最后需要特别指出的是,正则匹配默认是贪婪匹配,也就是匹配尽可能 多的字符。举例如下,匹配出数字后面的 0:

1
2
>>> re.match(r'^(\d+)(0*)$', '102300').groups()
('102300', '')

由于\d+采用贪婪匹配,直接把后面的 0 全部匹配了,结果 0*只能匹配 空字符串了。
必须让\d+采用非贪婪匹配(也就是尽可能少匹配),才能把后面的 0 匹配出来,加个?就可以让\d+采用非贪婪匹配:

1
2
>>> re.match(r'^(\d+?)(0*)$', '102300').groups()
('1023', '00')
CATALOG
  1. 1. 正则表达式(Regular Expression)#
    1. 1.1. 简单匹配#
      1. 1.1.1. 简单的匹配类型规则:#
      2. 1.1.2. 按类型匹配示例#
    2. 1.2. 灵活匹配方法#
    3. 1.3. 分组#
    4. 1.4. findall#
    5. 1.5. replace#
    6. 1.6. split#
    7. 1.7. compile#
    8. 1.8. 贪婪匹配#