python正则表达式的基本使用

432 阅读7分钟

正则表达式是编程过程中进行字符串模式识别、字符串处理的强力工具。学过一些自动机理论就知道正则表达式其实是有穷自动机,其可以识别的字符串集合称为其语言。正则表达式使用的是一种线性的、非递归的文法,并提供有限的上下文有关文法。

符号

  1. 字面值表达式

就是普通的,没有带任何标记的字符串

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
  1. 或表达式 re1|re2

用于匹配两个正则语言的并集 下面例子使用'-' 或 '|'来分割字符串

text = r'123/123-4212-4242/23543'

for item in re.split(r'(?:/|-)',text):
    print(item)

<<< 123
<<< 123
<<< 4212
<<< 4242
<<< 23543
  1. . 匹配任何字符(除了\n 之外)
text = r'123/123-42abc12-4242/23543'
print(re.findall(r'-.......-',text))

<<< ['-42abc12-']
  1. ^ 匹配字符串起始部分
text = r'123qwe123'

for it in re.finditer(r'^123',text):
    print(it.start())

<<< 0
  1. $ 匹配字符串终止部分

text = r'123qwe123'

for it in re.finditer(r'123$',text):
    print(it.start())

<<< 6
  1. * 匹配 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. + 匹配 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,之间可以有一个或多个空格

  1. ? 匹配 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,之间可以有一个空格,或者没有

  1. {N}匹配 N 次前面出现的正则表达式

将前面的大量重复改善

text = r'''\
Cool     Things
Cool Things
CoolThings
'''
print(re.findall(r'\w{4}\s*\w{6}',text))

<<< ['Cool     Things', 'Cool Things', 'CoolThings']
  1. {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
  1. […] 匹配来自字符集的任意单一字符

找出元音字母的位置


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
  1. [..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
  1. [^…] 不匹配此字符集中出现的任何一个字符,包括某一范围的字符(如果在此字符集中出现)

找出不含数字的非空白单词

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
  1. (…) 匹配封闭的正则表达式,然后另存为子组

有一点组合设计模式的味道,() 内也是完整的正则表达式,可以用于提取子组

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),'日')

<<< 19971127 日
<<< 1998522 日
<<< 1996229
  1. (*|+|?|{})? 用于匹配上面频繁出现/重复出现符号的非贪婪版本 (*、+、?、{})

其行为与贪婪相反,贪婪匹配会尽可能多的匹配字符,回溯的时候再减少匹配的字符。 非贪婪匹配会尽可能少的匹配,回溯的时候再增加匹配的字符。

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 的特殊符号,下面总结:

  1. \d 匹配任何十进制数字,与[0-9]一致(\D\d 相反,不匹配任何非数值型的数字)

    如: data\d+.txt匹配 data1.txt 、data2.txt ……

  2. \w 匹配任何字母数字字符,与[A-Za-z0-9_]相同(\W 与之相反)

    如: 标识符表示为 [a-zA-Z_]\w+

  3. \s 匹配任何空格字符,与[\n\t\r\v\f]相同(\S 与之相反)

    如: You\sare\sthe\winner

  4. \b 匹配任何单词边界(\B 与之相反)

    如: \bThe\b

  5. \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
  1. \c 逐字匹配任何特殊字符 c(即,仅按照字面意义匹配,不匹配特殊含义)

    c 包括 . \ ' [ ] { } ( ) ? + * 等用于正则表达式的标记符号

  2. \A(\Z) 匹配字符串的起始(结束)(另见上面介绍的^$

    如: \ADear

扩展正则表达式

  1. (?iLmsux) 在正则表达式中嵌入一个或者多个特殊“标记”参数(或者通过函数/方法)

    1. 使用时(?...)是一体的,置于正则表达式的最左端
    2. (?i) 用于忽略大小写,如前所示的元音字母就使用了r'(?i)[aeiou]'
    3. (?m) 实现跨行搜索,而不是将字符串看做整体,可以使 ^& 匹配每一行的首尾
    4. (?s) 使得 . 可以匹配换行符\n
    5. (?x) 该标记允许用户通过抑制在正则表达式中使用空白符(除了在字符类中或者在反斜线转义中)来创建更易读的正则表达式。
  2. (?:…) 表示一个匹配不用保存的分组

    使用该标记后,分组将不会被保存到matchObj.groups()中

  3. (?P<name>…) 像一个仅由 name 标识而不是数字 ID 标识的正则分组匹配

    该标记可以给分组起名字

  4. (?P=name) 在同一字符串中匹配由(?P=name)分组的之前文本

改写之前的例子

text = '''\
3.14  3.1415926 
'''

m = re.match(r'(?P<prefix>\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<name>来检索分组名

text = '''\
11/27/1997\
'''

res = re.sub(r'(?P<mon>\d+)/(?P<day>\d+)/(?P<year>\d+)',r'\g<day>/\g<mon>/\g<year>',text)
print(res)

<<< 27/11/1997
  1. (?#…) 表示注释,所有内容都被忽略

  2. (?=…) 匹配条件是如果…出现在之后的位置,而不使用输入字符串;称作正向前视断言

意思是匹配的时候向前尝试匹配后缀,如果后缀匹配成功,则匹配前缀

text = '''\
11/27/1997\
'''

for it in re.finditer(r'\d{1,2}/\d{1,2}(?=/\d{4})',text):
    print(it.group())

<<< 11/27
  1. (?!…) 匹配条件是如果…不出现在之后的位置,而不使用输入字符串;称作负向前视断言

(?=…) 相反,如果后缀不能匹配,这匹配前缀

  1. (?<=…) 匹配条件是如果…出现在之前的位置,而不使用输入字符串;称作正向后视断言

检查前缀,如果前缀匹配,则匹配后缀

text = '''\
ABC12345 qd7654\
'''
print(text)
print()
for it in re.finditer(r'(?<=[a-zA-Z]{3})\d+',text):
    print(it.group())

<<< 12345
  1. (?<!…) 匹配条件是如果…不出现在之前的位置,而不使用输入字符串;称作负向后视断言

检查前缀,如果前缀不匹配,则匹配后缀

  1. (?(id/name)Y|N ) 如果分组所提供的 id 或者 name(名称)存在,就返回正则表达式的条件匹配 Y,如果不存在,就返回 N|N是可选项