python-正则表达式

正则表达式(RE)是一些由字符和特殊字符组成的字符串,它们描述了这些字符和字符的某种重复方式,因此能按照某种模式匹配一个有相似特征的字符串的集合,因此能按某种模式匹配一系类有相似特征的字符串。

Python通过标准库re模块支持正则表达式。

说明一下术语“匹配”和“搜索”的区别:

Python专门术语中,有两种主要方法完成匹配:搜索和匹配。搜索:即在字符串任意部分中搜索匹配的模式。匹配:判断一个字符能否从开始处全部或部分的匹配某个模式。搜索通过search()函数或方法实现,匹配调用match()函数或方法实现。我们说模式的时候,使用的是术语“匹配”。

1.1 正则表达式使用的特殊符号和字符

记号

说明

正则表达式样例

literal

匹配字符串的值

foo

re1/re2

匹配正则表达式re1或re2

foo|bar

.

匹配任意字符(换行符除外)

b.b

^

匹配字符串的开始

^Dear

$

匹配字符串的结尾

/bin/*sh$

*

匹配前面出现的正则表达式零次或多次

[A-Za-z0-9]*

+

匹配前面出现的正则表达式出现一次或多次

[a-z]+\.com

?

匹配前面出现的正则表达式零次或一次

goo?

{N}

匹配前面出现的正则表达式N

[0-9]{3}

{M,N}

匹配重复出现M次到N次的正则表达式

[0-9]{5,9}

[…]

匹配字符组里出现的任意一个字符

[aeiou]

[.,x-y,.]

匹配从字符x到字符中的任意一个字符

[0-9],[A-Za-z]

[^…]

不匹配此字符集中出现的任意一个字符,包括某一范围的字符(如果在此字符集中出现)

[^aeiou],[^A-Za-z0-9]

(*|+|?|{})?

用于上面出现的任何“非贪婪”。版本重复匹配次数符号

.*?[a-z]

(…)

匹配封闭括号中正则表达式(RE),并保存为子组

([0-9]{3})?,f(oo|u)bar

特殊字符

\d

匹配任何数字,和[0-9]一样,(\D是\d的反义,任何非数符字)

data\d+.txt

\w

匹配任何数字字母字符,和[A-Za-z0-9_]相同,(\W是\w的反义)

[A-Za-z_]\w+

\s

匹配任何空白符,和[\n\t\r\v\f]相同。(\S是\s的反义)

of\sthe

\b

匹配单词边界。(\B是\b的反义)

\bThe\b

\nn

匹配已保存的子组

prince:\16

\c

逐一匹配特殊字符c(即:取消它的特殊含义,按字面匹配)

\.,\\, \*

\A(\Z)

匹配字符串的起始(结束)

\ADear

(1)用管道符号(|)匹配多个正则表达式模式

管道符号(|),表示一个或操作,它的意思是选择被管道符号分割的多个不同的正则表达式中的一个。

(2)匹配任意一个单个的字符(.)

点符号或句点(.)符号匹配除换行符(NEWLINE)外的任意一个单个字符(Python的正则表达式有一个编译标识[S or DOTALL]),该标识能去掉这一限制,使(.)      在匹配时包括换行符。无论是字母、数字、不包括“\n”的空白符、可打印的字符、还是非打印字符,或是一个符号,点(.)都可以匹配它们。

(3)从字符串的开头或结尾或单词边界开始匹配(^ $ \b \B)

^或\A:用于从字符串的开头开始匹配一个模式。

如果想匹配这两个字符中的任何一个(或全部),就必须用反斜线进行转义。

\b或\B:用来匹配单词边界,\b匹配的模式是一个单词边界(一定在一个单词的开头,不论这个单词的前面是有字符,还是没有字符),\B只匹配出现在一个单词中间的模式(不在单词边界上的字符)。

(4)创建字符类([])

使用方括号的正则表达式会匹配方括号里的任意一个字符。

(5)指定范围(-)和否定(^)

方括号里的一对字符中间的连字符(-)用来表示一个字符的范围。例如:z.[0-9]

如果左方括号后第一个字符是上箭头符号(^),就表示不匹配指定字符集里的任意字符。例如:[^aeiou]

(6)使用闭包操作符(*,+,?,{})实现多次出现/重复匹配

*:匹配它左边那个正则表达式出现零次或零次以上的情况。

+:匹配它左边那个正则表达式至少出现一次的情况。

?:匹配它左边那个正则表达式出现零次或一次的情况。

{N}:匹配N次出现。

{M,N}:匹配M次到N次出现。

(7)特殊字符表示

\d:0-9范围内的十进制数。

\w:整个字符数字的字符集,相当于“A-Za-z0-9_”。

\s:表示空白字符。

这些特殊字符的大写形式表示不匹配。

(8)用圆括号(())组建组

一对圆括号(())和正则表达式使用实现的功能有:1.对正则表达式进行分组;2.匹配子组。

使用圆括号匹配的子串会被保存到一个子组。这些字组可以在同一次匹配或搜索中被重复调用。

1.2 正则表达式和Python语言

1.2.1 re模块:核心函数和方法

常用的函数和方法:

函数/方法

描述

模块的函数

compile(pattern, flags=0)

对正则表达式模式pattern进行编译,flags是可选标识符,并返回一个regex对象(已编译的正则表达式对象)。

re模块的函数和regex对象的方法

match(pattern, string, flags=0)

尝试用正则表达式pattern匹配字符串string,flags是可选标识符,如果匹配成功,则返回一个匹配对象,否则返回None

search(pattern, string, flags=0)

在字符串string中搜索正则表达式模式pattern的第一次出现,flags是可选标识符,如果匹配成功,则返回一个匹配对象,否则返回None。

findall(pattern, string, [,flags])

在字符串string中搜索正则表达式模式pattern的所有(非重复)出现,返回一个匹配对象的列表。

finditer(pattern, string [,flags])

和findall()相同,但返回的不是列表而是迭代器;对于每个匹配,该迭代器返回一个匹配对象。

split(pattern, string, max=0)

根据正则表达式pattern中的分隔符把字符string分割为一个列表,返回成功匹配的列表,最多分割max次(默认是分割所有匹配的地方)。

sub(pattern, repl, string, max=0)

把字符串中所有匹配正则表达式pattern的地方替换成字符串repl,如果max的值没有给出,则对所有匹配的地方进行替换。

匹配对象的方法

group(num=0)

返回全部匹配对象(或指定编号是num的子组)

groups()

返回一个包含全部匹配的子组的元组(如果没有成功匹配,就返回一个空元祖)。

RE编译(何时应该使用compile函数?)

在模式匹配之前,正则表达式模式必须先被编译成regex对象,由于正则表达式在执行过程中被多次用于比较,建议先对它做预编译。re.compile()函数提供了此功能。

1.2.2 匹配对象和group()、groups()方法

在处理正则表达式中,出regex对象外,还有一种对象类型-匹配对象,通过match()函数和search()函数被成功调用后返回。匹配对象有两个主要方法:group()和groups()。

group():或返回所有匹配对象或根据要求返回某个特定子组。

groups():返回一个包含唯一或所有子组的元组。

1.2.3 用match()匹配字符串

match():从字符串开头开始对模式进行匹配,如果匹配成功,返回一个匹配对象,如果失败,返回None。匹配对象的group()方法可以用来显示那个成功的匹配。

 1 m = re.match('foo', 'foo')
 2 >>> if m is not None: 
 3 ...     m.group()    
 4 ... 
 5 'foo'
 6 >>> m
 7 <_sre.SRE_Match object at 0x7f4e059a9648>
 8 >>> m = re.match('foo', 'food on table') 
 9 >>> m.group()
10 'foo'

1.2.4 search()在一个字符串中查找一个模式

search():检查参数字符串任意位置的地方给定正则表达式的匹配情况,如果搜索到成功的匹配,会返回一个匹配对象,否则返回None。

1 >>> m = re.match('foo', 'seafood')
2 >>> m.group()
3 Traceback (most recent call last):
4   File "<stdin>", line 1, in <module>
5 AttributeError: 'NoneType' object has no attribute 'group'
6 >>> m = re.search('foo', 'seafood').group()
7 >>> m
8 'foo'

1.2.5 匹配多个字符串(|)

 1 >>> bt = 'bat|bet|bit'
 2 >>> m = re.match(bt, 'bat')
 3 >>> m
 4 <_sre.SRE_Match object at 0x7f4e059a9ac0>
 5 >>> m.group()
 6 'bat'
 7 >>> m = re.match(bt, 'bit')
 8 >>> m.group()              
 9 'bit'
10 >>> m = re.match(bt, 'bet')
11 >>> m.group()              
12 'bet'

1.2.6 匹配任意单个字符(.)

句点是不能匹配换行符或非字符(即空字符)的。

 1 >>> m = re.match(anyend, 'bend')
 2 >>> if m is not None: m.group()   
 3 ... 
 4 'bend'
 5 >>> m = re.match(anyend, '\nend') 
 6 >>> if m is not None: m.group()   
 7 ... 
 8 >>> m = re.match(anyend, 'end')
 9 >>> if m is not None: m.group()
10 ... 
11 >>> m = re.match(anyend, 'the end')
12 >>> 
13 >>> if m is not None: m.group()    
14 ... 
15 >>>
16 >>> patt314 = '3.14'
17 >>> pi_patt = '3\.14'
18 >>> m = re.match(pi_patt, '3.14')
19 >>> if m is not None:m.group()
20 ... 
21 '3.14'
22 >>> m = re.match(patt314, '3014')    
23 >>> if m is not None:m.group()   
24 ... 
25 '3014'
26 >>> m = re.match(patt314, '3.14')
27 >>> if m is not None:m.group()   
28 ... 
29 '3.14'
30 >>>

1.2.7 创建字符合集([])

 1 >>> m = re.match('[cr][23][dp][o2]', 'c3po')
 2 >>> if m is not None:m.group()              
 3 ... 
 4 'c3po'
 5 >>> m = re.match('[cr][23][dp][o2]', 'c2po')
 6 >>> 
 7 >>> if m is not None:m.group()              
 8 ... 
 9 'c2po'
10 >>> m = re.match('r2d2|c3po', 'c2po')                   
11 >>> 
12 >>> if m is not None:m.group()       
13 ... 
14 >>> m = re.match('r2d2|c3po', 'r2po')
15 >>> if m is not None:m.group()       
16 ... 
17 >>> m = re.match('r2d2|c3po', 'r2d2')
18 >>> 
19 >>> if m is not None:m.group()       
20 ... 
21 'r2d2'
22 >>>

1.2.8 重复、特殊字符和子组

例如下面的例子,表达式容许“.com”前面有一个或两个名字:

1 >>> patt = '\w+@(\w+\.)?\w+\.com' 
2 >>> re.match(patt, 'nobody@xxx.com').group()
3 'nobody@xxx.com'

接着修改,允许任意数量的子域存在。

1 >>> patt = '\w+@(\w+\.)*\w+\.com' 
2 >>> re.match(patt, 'nobody@xxx.yyy.zzz.com').group() 
3 'nobody@xxx.yyy.zzz.com'

分别提取包含字母或数字的部分或仅含有数字的部分。

1 >>> m = re.match('(\w\w\w)-(\d\d\d)', 'abc-123') 
2 >>> m.group()
3 'abc-123'
4 >>> m.group(1) 
5 'abc'
6 >>> m.group(2) 
7 '123'
8 >>> m.groups() 
9 ('abc', '123')

group()通常用来显示所有匹配部分,也可以用来获取个别匹配的子组,groups()方法获得一个包含所有匹配子组的元组。

 1 >>> m = re.match('ab', 'ab') 
 2 >>> m.group()
 3 'ab'
 4 >>> m.groups() 
 5 ()
 6 >>> m = re.match('(ab)', 'ab') 
 7 >>> m.group()  
 8 'ab'
 9 >>> m.group(1) 
10 'ab'
11 >>> m.groups() 
12 ('ab',)
13 >>> m = re.match('(a)(b)', 'ab') 
14 >>> m.group()  
15 'ab'
16 >>> m.group(1) 
17 'a'
18 >>> m.group(2) 
19 'b'
20 >>> m.groups() 
21 ('a', 'b')
22 >>> m = re.match('(a(b))', 'ab') 
23 >>> m.group()  
24 'ab'
25 >>> m.group(1) 
26 'ab'
27 >>> m.group(2) 
28 'b'
29 >>> m.groups() 
30 ('ab', 'b')

1.2.9 从字符串的开头或结尾匹配及在单词边界上的匹配

这些是锚点性正则表达式,这些锚点性正则表达式主要用于搜素而不是匹配,因为match()总是从字符串的开头进行匹配的。

 1 >>> m = re.search('^The', 'The end')  #在开头
 2 >>> if m is not None:m.group()
 3 ... 
 4 'The'
 5 >>> m = re.search('^The', 'end. The') 
 6 >>> if m is not None:m.group()        
 7 ... 
 8 >>> m = re.search(r'\bthe', 'bite the dog')  #在词边界
 9 >>> if m is not None:m.group()
10 ... 
11 'the'
12 >>> m = re.search(r'\bthe', 'bitethe dog')  
13 >>> if m is not None:m.group()
14 ... 
15 >>> m = re.search(r'\Bthe', 'bitethe dog')  #不在词边界
16 >>> if m is not None:m.group()
17 ... 
18 'the'

1.2.10 用findall()找到每个出现的匹配部分

findall():用于非重叠的搜索某字符串中一个正则表达式模式出现的情况。返回的总是一个列表,如果没有匹配,返回空列表;如果成功找到匹配部分,则返回所有匹配部分的列表(按从左到右出现的顺序排列)。

1 >>> re.findall('car', 'car') 
2 ['car']
3 >>> re.findall('car', 'scary') 
4 ['car']
5 >>> re.findall('car', 'carry the barcardi to the car') 
6 ['car', 'car', 'car']

1.2.11 用sub()(和subn())进行搜索和替换

两者几乎一样,都是将某字符串中所有匹配正则表达式模式的部分进行替换。用来替换的部分通常是一个字符串,但也可能是一个函数,该函数返回一个用来替换的字符串。

subn()还返回一个表示替换次数的数字,替换后的字符串和表示替换次数的数字作为一个元组的元素返回。

 1 >>> re.sub('X', 'Mr.Smith', 'attn: X\n\nDear X,\n') 
 2 'attn: Mr.Smith\n\nDear Mr.Smith,\n'
 3 >>> re.subn('X', 'Mr.Smith', 'attn: X\n\nDear X,\n') 
 4 ('attn: Mr.Smith\n\nDear Mr.Smith,\n', 2)
 5 >>> print re.sub('X', 'Mr.Smith', 'attn: X\n\nDear X,\n') 
 6 attn: Mr.Smith
 7 
 8 Dear Mr.Smith,
 9 
10 >>> re.sub('[ae]', 'X', '[abcdef]') 
11 '[XbcdXf]'
12 >>> re.subn('[ae]', 'X', '[abcdef]') 
13 ('[XbcdXf]', 2)
14 >>> 

1.2.12 用split()分隔(分隔模式)

根据固定的字符串分隔。

1 >>> re.split(':', 'str1:str2:str3') 
2 ['str1', 'str2', 'str3']

例子:Linux下who命令输出结果进行分隔

#!/usr/bin/env python

from os import popen
from re import split

f = popen('who', 'r')
for eachline in f.readlines():
    print split('\s\s+|\t', eachline.strip())
f.close()
输出结果:
[root@localhost tmp]# python rewho.py 
['grace', 'tty1', '2020-01-26 14:18 (:0)']
['grace', 'pts/0', '2020-01-26 14:18 (:0.0)']
['root', 'pts/1', '2020-01-26 14:19 (192.168.230.1)']

核心笔记:python原始字符串的用法

原生字符串的产生是由于正则表达式的存在,由于ASCLL字符和正则表达式特殊字符间产生的冲突。例如“\b”在ASCLL字符中代表退格符,但同时也是一个正则表达式的特殊字符,代表“匹配一个单词边界”。为了让RE编辑器把两个字符‘\b’当成想要表达的字符串,可以是用r’\b’。

 1 >>> m = re.match('\bblow', 'blow')
 2 >>> if m is not None:m.group()
 3 ... 
 4 >>> m = re.match('\\bblow', 'blow')
 5 >>> 
 6 >>> if m is not None:m.group()     
 7 ... 
 8 'blow'
 9 >>> m = re.match(r'\bblow', 'blow') 
10 >>> if m is not None:m.group()     
11 ... 
12 'blow'

1.3 正则表达式示例

1.3.1 正则表达式示例

下面展示使用正则表达式处理字符串的不同办法。第一步:拿出一段代码用来生成随机数据,生成的数据用于以后操作。

 1 from random import randint, choice
 2 from string import lowercase
 3 from sys import maxint
 4 from time import ctime
 5 
 6 doms = ['com', 'edu', 'net', 'org', 'gov']
 7 
 8 for i in range(randint(5, 10)):
 9     dtint = randint(0, maxint-1)
10     dtstr = ctime(dtint)
11     shorter = randint(4, 7)
12     em = ''
13     for j in range(shorter):
14         em += choice(lowercase)
15 
16     longer = randint(shorter, 12)
17     dn = ''
18     for j in range(longer):
19         dn += choice(lowercase)
20     
21     print '%s::%s@%s.%s::%d-%d-%d' % (dtstr, em, dn, choice(doms), dtint, shorter, longer)

接下来使用生成的字符串来进行测试:

测试1:提取时间戳中的有关星期的数据字段

 1 >>> data = 'Fri Sep  7 08:10:38 2035::vjxod@dgolkl.edu::2072736638-5-6'
 2 >>> patt = '^(Mon|Tue|Wed|Thu|Fri|Sat|Sun)'
 3 >>> m = re.match(patt, data)
 4 >>> m.group()
 5 'Fri'
 6 >>> m.group(1)
 7 'Fri'
 8 >>> m.groups()
 9 ('Fri',)
10 >>>

测试2:测试1例子的宽松表示

1 >>> data
2 'Fri Sep  7 08:10:38 2035::vjxod@dgolkl.edu::2072736638-5-6'
3 >>> patt = '^(\w{3})'
4 >>> m = re.match(patt, data)
5 >>> m.group()
6 'Fri'
7 >>> m.group(1)
8 'Fri'
9 >>>

1.3.2 搜索与匹配的比较,“贪婪”匹配

例如我们要搜索三个连字符号(-)分隔的整型集。

 1 >>> data
 2 'Fri Sep  7 08:10:38 2035::vjxod@dgolkl.edu::2072736638-5-6'
 3 >>> patt = '\d+-\d+-\d'
 4 >>> re.search(patt, data).group()
 5 '2072736638-5-6'
 6 >>> patt = '.+\d+-\d+-\d+'
 7 >>> re.match(patt, data).group()
 8 'Fri Sep  7 08:10:38 2035::vjxod@dgolkl.edu::2072736638-5-6'
 9 >>> patt = '.+(\d+-\d+-\d+)'    
10 >>> re.match(patt, data).group()
11 'Fri Sep  7 08:10:38 2035::vjxod@dgolkl.edu::2072736638-5-6'
12 >>> re.match(patt, data).group(1)
13 '8-5-6'

出现上面的原因是:正则表达式本身默认是贪心匹配的。也就是说,如果正则表达式中使用到通配字,那它在按照从左到右的顺序求值时,会尽量“抓取”满足匹配的最长字符串。在上面的例子中,“.+”会从字符串的开始处抓取满足模式的最长字符,其中包含我们想得到的第一个整型字符起始到这个第一位数字“8”之间的所有字符。

一个解决办法是用“非贪婪”操作符“?”。这个操作符可以用在“*”、“+”或“?”的后面。它的作用是要求正则表达式引擎匹配的字符越少越好。

1 >>> patt = '.+?(\d+-\d+-\d+)'    
2 >>> re.match(patt, data).group(1)
3 '2072736638-5-6'
4 >>>

示例3:假设我们只想抽取三个整型字段里中间的那个整型部分

1 >>> data = 'Fri Sep  7 08:10:38 2035::vjxod@dgolkl.edu::2072736638-5-6'
2 >>> patt = '-(\d+)-'
3 >>> m = re.search(patt, data)
4 >>> m.group()
5 '-5-'
6 >>> m.group(1)
7 '5'
8 >>>
posted @ 2020-01-26 22:50  zhengcixi  阅读(348)  评论(0编辑  收藏  举报
回到顶部