python核心编程第3版第1章 正则表达式【读书笔记】

什么是正则表达式

正则表达式为高级的本文模式匹配、抽取、与/或文本形式的搜索和替换功能提供了基础。简单地说,正则表达式(简称为regex)是一些由字符和特殊符号组成的字符串。

re模块

python通过标准库中的re模块来支持正则表达式

搜索和匹配的比较

“搜索”即在字符串任意部分中搜索匹配的模式,通过search()函数或方法来实现,搜索试图从字符串的任何位置开始匹配;
“匹配”是指判断一个字符串能否从起始处全部或者部分的匹配某个模式,通过调用match()函数或方法实现,匹配试图在字符串的开始位置进行匹配。

[A-Za-z]\w+的含义

[A-Za-z]\w+的含义是第一个字符是字母,也就是说要么A-Z,要么a-z,后面是至少一个(+)由字母数字组成的字符(\w)。

没有使用任何特殊字符去匹配其他符号的模式

正则表达式模式 匹配的字符串
foo foo
python python
abc123 abc123

该模式没有使用任何特殊字符去匹配其他符号,而只匹配所描述的内容。能够匹配这个模式的只有包含“foo”的字符串。同理,对于字符串“python”和“abc123”也一样。

特殊符号和字符(元字符)

常见正则表达式符号和特殊字符

表示法 描述 正则表达式示例
符号
literal 匹配文本字符串的字面值literal foo
re1 | re2 匹配正则表达式re1或者re2 foo | bar
. 匹配任何字符(除了\n之外) b.b
^ 匹配字符串起始部分 ^Dear
$ 匹配字符串终止部分 /bin/*sh$
* 匹配0次或者多次前面出现的正则表达式 [A-Za-z0-9]*
+ 匹配1次或者多次前面出现的正则表达式 [a-z]+\.com
? 匹配0次或者1次前面出现的正则表达式 goo?
{N} 匹配N次前面出现的正则表达式 [0-9]{3}
{M,N} 匹配M-N次前面出现的正则表达式 [0-9]{5,9}
[...] 匹配来自字符集的任意单一字符 [aeiou]
[..x-y..] 匹配x-y范围内的任意单一字符 [0-9],[A-Za-z]
[^...] 不匹配此字符集中出现的任何一个字符,包括某一范围的字符(如果在此字符集中出现) [aeiou],[A-Za-z0-9]
(*|+|?|{})? 用于匹配上面频繁出现/重复出现符号的非贪婪版本(*、+、?、{}) .*?[a-z]
(...) 匹配封闭的正则表达式,然后另存为子组 ([0-9]{3})?,f(oo|u)bar
特殊字符
\d 匹配任何十进制数字,与[0-9]一致(\D与\d相反,不匹配任何非数值型的数字) data\d+.txt
\w 匹配任何字符数字字符,与[A-Za-z0-9_]相同(\W与之相反) [A-Za-z_]\w+
\s 匹配任何空格字符,与[\n\t\r\v\f]相同(\S与之相反) of\sthe
\b 匹配任何单词边界(\B与之相反) \bThe\b
\N 匹配已保存的子组N(参见上面的(...)) price:\16
\c 逐字匹配任何特殊字符c(即,仅按照字面意义匹配,不匹配特殊含义) \.,\\,\*
\A(\Z) 匹配字符串的起始(结束) \ADear
扩展表示法
(?iLmsux) 在正则表达式中嵌入一个或者多个特殊“标记”参数(或者通过函数/方法) (?x),(? im)
(?:...) 表示一个匹配不用保存的分组 (?:\w+.)*
(?P< name >...) 像一个仅由name标识而不是数字ID标识的正则分组匹配 (?P< data >)
(?P=name) 在同一个字符串中匹配由 (?P< name)分组的之前文本 (?P=data)
(?#...) 表示注释,所有内容都被忽视 (?#comment)
(?=...) 匹配条件是如果...出现在之后的位置,而不使用输入字符串;称为正向前视断言 (?=.com)
(?!...) 匹配条件是如果...不出现在之后的位置,而不使用输入字符串;称为负向前视断言 (?!.net)
(?<=...) 匹配条件是如果...出现在之前的位置,而不使用输入字符串;称为正向后视断言 (?<=800_)
(? 匹配条件是如果...不出现在之前的位置,而不是用输入字符串;称为负向后视断言 (?
(? (id/name)Y|N) 如果分组所提供的id或者name(名称)存在,就返回正则表达式的条件匹配Y,如果不存在,就返回N;|N是可选项 (?(1)y|x)

使用择一匹配符号匹配多个正则表达式模式 |

表示择一匹配的管道符号(|),表示一个“从多个模式中选择其一”的操作,它用于分隔不同的正则表达式。
如正则表达式模式为 at|home,匹配的字符串为 at、home

匹配任意单个字符 .

点号或者句点(.)符号匹配除了换行符\n以外的任何字符。

正则表达式模式 匹配的字符串
f.o 匹配在字母“f”和“o”之间的任意一个字符;例如fao、f9o、f#o等
.. 任意两个字符
.end 匹配在字符串end之前的任意一个字符

从字符串起始或者结尾或者单词边界匹配 ^ $ \b \B

正则表达式模式 匹配的字符串
^From 任何以From为起始的字符串
/bin/tcsh$ 任何以/bin/tcsh作为结尾的字符串
^Subject:hi$ 任何由单独的字符串Subject:hi构成的字符串
the 任何包含the的字符串
\bthe 任何以the开始的字符串
\bthe\b 仅仅匹配单词the
\Bthe 任何包含但并不以the作为起始的字符串

创建字符集(适用单字符) []

正则表达式模式 匹配的字符串
b[aeiou]t bat、bet、bit、but
[cr][23][dp][o2] 一个包含 4 个字符的字符串: 第一个字符是 “r” 或 “c”,然后是 “2” 或 “3”,后面是 “d” 或 “p”,最后是 “o” 或 “2“ ,例 如:c2do, r3p2, r2d2, c3po, 等等。

限定范围和否定 - ^

正则表达式模式 匹配的字符串
z.[0-9] 字符"z"后面跟任意一个字符,然后是一个十进制数字
[r-u][env-y][us] 先是字母 “r” “s,” “t” 或 “u” 中的任意一个字符,后面跟的是 “e,” “n,” “v,” “w,” “x,” 或 “y”中的任意一个字符,再后面是字符“u” 或 “s”
[^aeiou] 一个非元音字符
[^\t\n] 除TAB制表符和换行符以外的任意一个字符
["-a] 在使用 ASCII 字符集的系统中,顺序值在'"'和'a'之间的任意一个字符,即34-97之间的某一个字符。

使用闭包操作符实现存在性和频数匹配 *, +, ?, {}

星号或称星号操作符()*匹配其左边那个正则表达式出现零次或零次以上的情况(在计算机语言和编译器原理里,此操作符被叫做 Kleene 闭包操作符)。 * >=0
加号(+)操作符匹配一次或多次出现的正则表达式(也被称为正闭包操作符)。+>=1
问号操作符( ? )匹配零次或者一次出现的正则表达式。 ?=[0,1]
花括号操作符({ }),花括号里可以是单个的值,也可以是由逗号分开的一对值。如果是 一个值,如,{N},则表示匹配 N 次出现;如果是一对值,即,{M, N},就表示匹配 M 次到 N 次出现。

正则表达式模式 匹配的字符串
[dn]ot? 字符"d"或"n", 后面跟着一个"o", 最后是最多一个"t",即,do, no, dot, not
0?[1-9] 任何数值数字,它可能前置一个“0”。例如:可以把它看成一月到九月的数字表示形式,不管是一位数字还是两位数字的表示形式。
[0-9]{15,16} 匹配15或者16个数字,例如:信用卡号码
< /?[^>]+> 匹配所有有效的(和无效的)HTML 标签的字符串
[KQRBNP][a-h][1-8]-[a-h][1-8] 在“长代数”标记法中,表示的国际象棋合法的棋盘移动(仅移动,不包括吃子和将军)。 即, “K,” “Q,” “R,” “B,” “N,” 或 “P” 等字母后面加上两个用连字符连在一起的"a1"到"h8"之间的棋盘坐标。前面的坐标表示从哪里开始走棋,后面的坐标代表走到哪个位置(棋格)去。

表示字符集的特殊字符

“\d”表示“0–9”。
“\w” 可用来表示整个字符数字的字符集,即相当于“[A-Za-z0-9_]”的缩写形式
“\s” 代表空白字符。
这些特殊字符的大写形式表示不匹配,比如,“\D” 表示非十进制数字的字符(等价于 “[^0-9]”)。

正则表达式模式 匹配的字符串
\w+-\d+ 一个由字母或数字组成的字符串和一串由一个连字符分隔的数字
[A-Za-z]\w* 第一个字符是字母,其余字符(如果存在的话),是字母或数字
\d{3}-\d{3}-\d{4} (美国)电话号码,前面带区号前缀,例如 800-555-1212
\w+@\w+.com 简单的 [email protected] 格式的电子邮件地址

使用圆括号指定分组 ()

正则表达式模式 匹配的字符串
\d+(\.\d*)? 表示简单浮点数的字符串,即,任何十进制数字,后面跟一个可选的小数点,然后再接零或多个十进制数字。例如:“0.004,” “2,” “75.”, 等等。
(Mr?s?\.)?[A-Z][a-z]*[A-Za-z-]+ 名字和姓氏,以及对名字的限制(如果存在,首字母大写,其它字母小写), 全名前有可选的称谓(“Mr.”, “Mrs.” ,“Ms.” ,或 “M.”),姓氏没有什么限制,允许有多个单词、横线、大写字母。

扩展表示法 (?...)

正则表达式模式 匹配的字符串
(?:\w+\.)* 以句点作为结尾的字符串,例如“google.”、“twitter.”、“facebook.”,但是这些匹配不会保存下来供后续的使用和数据检索
(?#comment) 此处并不做匹配,只是作为注释
(?=.com) 如果一个字符串后面跟着“.com”才做匹配操作,并不使用任何目标字符串
(?!.net) 如果一个字符串后面不是跟着“.net”才做匹配
(?<=800-) 如果字符串之前为“800-”才做匹配,假定为电话号码,同样,并不使用任何输入字符串
(? 如果一个字符串之前不是“192.168”才做匹配操作,假定用于过滤一组C类IP地址
(?(1)y|x) 如果一个匹配组1(\1)存在,就与y匹配;否则,就与x匹配

正则表达式和python语言

常见的正则表达式属性

函数/方法 描述
仅仅是re模块函数
compile(pattern,flags=0) 使用任何可选的标记来编译正则表达式的模式,然后返回一个正则表达式对象
re 模块的函数和正则表达式对象的方法
match(pattern,string, flags=0) 尝试使用带有可选的标记的正则表达式的模式来匹配字符串。如果匹配成功,就返回匹配对象;如果失败,就返回None
search(pattern,string, flags=0) 尝试可选标记搜索字符串中第一次出现的正则表达式模式。如果匹配成功,就返回匹配对象;如果失败,就返回None
findall(pattern,string[,flags]) 查看字符串中所有(非重复)出现的正则表达式模式,并返回一个匹配列表
finditer(pattern,string[, flags]) 与findall()函数相同,但返回的不是一个列表,而是一个迭代器。对于每一次匹配,迭代器都返回一个匹配对象
split(pattern,string, max=0) 根据正则表达式的模式分隔符,split函数将字符串分割为列表,然后返回成功匹配的列表,分割最多操作max次(默认分割所有匹配成功的位置)
sub(pattern, repl, string, max=0) 使用repl替换所有正则表达式的模式在字符串中出现的位置,除非定义count,否则就将替换所有出现的位置
purge() 消除隐式编译的正则表达式模式
常见的匹配方法
group(num=0) 返回整个匹配对象,或者编号为num的特定子组
groups(default=None) 返回一个包含所有匹配子组的元组(如果没有成功匹配,则返回一个空元组)
groupdict(default=None) 返回一个包含所有匹配的命名子组的字典,所有的子组名称作为字典的键(如果没有成功匹配,则返回一个空字典)
常用的模块属性
re.I、re.IGNORECASE 不区分大小写的匹配
re.L、re.LOCALE 根据所使用的本地语言环境通过\w、\W、\b、\B、\s、\S实现匹配
re.M、re.MULTILINE ^和$分别匹配目标字符串中行的起始和结尾,而不是严格匹配整个字符串本身的起始和结尾
re.S、re.DOTALL “.”(点号)通常匹配除了\n(换行符)之外的所有单个字符;该标记表示“.”(点号)能够匹配全部字符
re.X、re.VERBOSE 通过反斜线转义,否则所有空格加上#都被忽略,除非在一个字符类中或者允许注释并且提高可读性

使用 compile()编译正则表达式

对于一些特别的正则表达式编译,可选的标记可能以参数的形式给出,这些标记允许不区分大小写的匹配,他们可以通过按位或操作符(|)合并
这些标记也可以作为参数适用于大多数re模块函数。如果想要在方法中使用这些标记,它们必须集成到已编译的正则表达式对象之中,或者需要使用直接嵌入到正则表达式本身(?F)的标记,其中F是一个或者多个i(用于re.I/re.IGNORECASE)、m(用于re.M/re.MULTILINE)、s(用于re.DOTALL)等。如果想要同时使用多个,就把它们放在一起而不是使用按位或操作,例如,(?im)可以用于同时表示re.I/re.IGNORECASE和re.M/re.MULTILINE。

匹配对象以及group()和groups()方法

匹配对象是成功调用match()或者search()返回的对象。
匹配对象有两个主要的方法:group()和groups()。
group()要么返回整个匹配对象,要么根据要求返回特定子组,
groups()则仅返回一个包含唯一或者全部子组的元组。

使用match()方法匹配字符串

match()方法试图从字符串的起始部分对模式进行匹配。如果匹配成功,就返回匹配对象;如果失败,就返回None,匹配对象的group()方法用于显示那么成功的匹配。

>>> import re
>>> m=re.match('foo','foo').group()
>>> if m is not None:
...     m
...
'foo'

在实际操作中,最好不要省区if语句块,以避免AttributeError异常(None是返回的错误值,该值并没有group()方法)。

>>> m=re.match('foo','bar').group()
Traceback (most recent call last):
  File "", line 1, in 
AttributeError: 'NoneType' object has no attribute 'group'

只要模式从字符串的起始部分开始匹配,即使字符串比模式长,匹配也仍然能够成功。

>>> m=re.match('foo','food on').group()
>>> m
'foo'

使用search()在一个字符串中查找模式(搜索与匹配的对比)

search()用于在任意位置对给定正则表达式模式搜索第一次出现的匹配情况,如果搜索到成功的匹配,就会返回一个匹配对象;否则,返回None。

搜索与匹配的对比

>>> m=re.match('foo','seafood')#匹配失败
>>> if m is not None:
...     m.group()
...
>>> m
>>> m=re.search('foo','seafoof')#搜索成功,但匹配失败
>>> if m is not None:
...     m.group()
...
'foo'

匹配多个字符串

>>> bt='bat|bet|bit'   #正则表达式模式:bat bet bit
>>> m=re.match(bt,'bat')    #是匹配
>>> if m is not None:
...     m.group()
...
'bat'
>>> m=re.match(bt,'blt')    #没有匹配
>>> if m is not None:    m.group()
...
>>> m=re.match(bt,'He bit me!')   #不能匹配字符串
>>> if m is not None:    m.group()
...
>>> m=re.search(bt,'He bit me!')   #搜索成功
>>> if m is not None:    m.group()
...
'bit'

匹配任意单个字符

>>> anyend='.end'
>>> m=re.match(anyend,'bend')   #点好匹配‘b’
>>> if m is not None:    m.group()
...
'bend'
>>> m=re.match(anyend,'end')     #不匹配任何字符
>>> if m is not None:    m.group()
...
>>> m=re.match(anyend,'\nend')    #除了/n之外的任何字符
>>> if m is not None:    m.group()
...
>>> m=re.search('.end','The end.')   #在搜索中匹配‘ ’
>>> if m is not None:    m.group()
...
' end'
>>> patt314='3.14'
>>> pi_patt='3\.14'
>>> m=re.match(pi_patt,'3.14')    #精确匹配
>>> if m is not None:    m.group()
...
'3.14'
>>> m=re.match(patt314,'3014')    #点号匹配‘0’
>>> if m is not None:    m.group()
...
'3014'
>>> m=re.match(patt314,'3.14')     #点好匹配‘.’
>>> if m is not None:    m.group()
...
'3.14'

创建字符集 [ ]

>>> m=re.match('[cr][23][dp][o2]','c3po')  #匹配
>>> if m is not None:    m.group()
...
'c3po'
>>> m=re.match('[cr][23][dp][o2]','c2do')  #匹配
>>> if m is not None:    m.group()
...
'c2do'
>>> m=re.match('r2d2|c3po','c2do')  #不匹配
>>> if m is not None:    m.group()
...
>>> m=re.match('r2d2|c3po','r2d2')  #匹配
>>> if m is not None:    m.group()
...
'r2d2'

重复、特殊字符以及分组

>>> patt='\w+@(\w+\.)?\w+\.com'
>>> re.match(patt,'[email protected]').group()
'[email protected]'
>>> re.match(patt,'[email protected]').group()
'[email protected]'
>>> patt='\w+@(\w+\.)*\w+\.com'
>>> re.match(patt,'[email protected]').group()
'[email protected]'
>>> m=re.match('\w\w\w-\d\d\d','abc-123')
>>> if m is not None:    m.group()
...
'abc-123'
>>> m=re.match('\w\w\w-\d\d\d','abc-xyz')
>>> if m is not None:    m.group()
...

group() groups()用法

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

>>> m=re.match('(\w\w\w)-(\d\d\d)','abc-123')
>>> m.group()   #完整匹配
'abc-123'
>>> m.group(1)  #子组1
'abc'
>>> m.group(2)  #子组2
'123'
>>> m.groups()  #全部子组
('abc', '123')
>>> m=re.match('ab','ab')  #没有子组
>>> m.group()
'ab'
>>> m.groups()
()
>>> m=re.match('(ab)','ab')  #一个子组
>>> m.group()
'ab'
>>> m.group(1)
'ab'
>>> m.groups()
('ab',)
>>> m=re.match('(a)(b)','ab')  #两个子组
>>> m.group()
'ab'
>>> m.group(1)
'a'
>>> m.group(2)
'b'
>>> m.groups()
('a', 'b')
>>> m=re.match('(a(b))','ab')  #两个子组
>>> m.group()
'ab'
>>> m.group(1)
'ab'
>>> m.group(2)
'b'
>>> m.groups()
('ab', 'b')

匹配字符串的起始和纪委以及单词边界

>>> m=re.search('^The','The end.')   #匹配
>>> if m is not None:    m.group()
...
'The'
>>> m=re.search('^The','end The.')    #不作为起始
>>> if m is not None:    m.group()
...
>>> m=re.search(r'\bthe','bite the dog')  #在边界
>>> if m is not None:    m.group()
...
'the'
>>> m=re.search(r'\bthe','bitethe dog')   #有边界
>>> if m is not None:    m.group()
...
>>> m=re.search(r'\Bthe','bitethe dog')   #没有边界
>>> if m is not None:    m.group()
...
'the'

使用findall()和finditer()查找每一次出现的位置

findall()查询字符串中某个正则表达式模式全部的非重复出现情况,总是返回一个列表。如果findall()没有找到匹配的部分,就返回一个空列表,但如果匹配成功,列表将包含所有成功的匹配部分(从左到右按出现顺序排列)。

>>> re.findall('car','car')
['car']
>>> re.findall('car','scary')
['car']
>>> re.findall('car','carry the barcardi to the car')
['car', 'car', 'car']

finditer()在匹配对象中迭代。

在单个字符串中执行两个不同分组的匹配

>>> s='This and that.'
>>> re.findall(r'(th\w+) and (th\w+)',s,re.I)
[('This', 'that')]
>>> re.finditer(r'(th\w+) and (th\w+)',s,re.I).next().groups()
('This', 'that')
>>> re.finditer(r'(th\w+) and (th\w+)',s,re.I).next().group(1)
'This'
>>> re.finditer(r'(th\w+) and (th\w+)',s,re.I).next().group(2)
'that'
>>> [g.groups() for g in re.finditer(r'(th\w+) and (th\w+)',s,re.I)]
[('This', 'that')]

在单个字符串中执行单个分组的多重匹配

使用finditer()函数完成的所有额外工作都旨在获取它的输出来匹配findall()的输出。

>>> re.findall(r'(th\w+)',s,re.I)
['This', 'that']
>>> it=re.finditer(r'(th\w+)',s,re.I)
>>> g=it.next()
>>> g.groups()
('This',)
>>> g.group(1)
'This'
>>> g=it.next()
>>> g.groups()
('that',)
>>> g.group(1)
'that'
>>> [g.group(1) for g in re.finditer(r'(th\w+)',s,re.I)]
['This', 'that']

使用sub()和subn()搜索与替换

sub()实现搜索与替换功能,返回一个用来替换的字符串。
subn()实现搜索与替换功能,返回一个用来替换的字符串以及一个表示替换的总数,替换后的字符串和表示替换总数的数字一起作为一个拥有两个元素的元组返回。

>>> re.sub('X','Mr.Smith','attn: X\n\nDear X,\n')
'attn: Mr.Smith\n\nDear Mr.Smith,\n'
>>> re.subn('X', 'Mr. Smith', 'attn: X\n\nDear X,\n')
('attn: Mr. Smith\n\nDear Mr. Smith,\n', 2)
>>> print re.sub('X', 'Mr. Smith', 'attn: X\n\nDear X,\n')
attn: Mr. Smith

Dear Mr. Smith,

>>> re.sub('[ae]','X','abcdef')
'XbcdXf'
>>> re.subn('[ae]','X','abcdef')
('XbcdXf', 2)

将美式的日期表示法格式转换为其他国家常用的格式

将美式的日期表示法MM/DD/YY{,YY}格式转换为其他国家常用的格式DD/MM/YY{,YY}。
\N,N是在替换字符串中使用的分组编号。

>>> re.sub(r'(\d{1,2})/(\d{1,2})/(\d{2}|\d{4})',r'\2/\1/\3','2/20/91')
'20/2/91'
>>> re.sub(r'(\d{1,2})/(\d{1,2})/(\d{2}|\d{4})',r'\2/\1/\3','2/20/1991')
'20/2/1991'

在限定模式上使用split()分割字符串

如果给定分隔符不是使用特殊符号来匹配多重模式的正则表达式,那么re.split()与str.split()的工作方式相同。

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

如果空格紧跟在五个数字或者两个大写字母(美国联邦州缩写)之后,就用split语句分割该空格。

>>> import re
>>> DATA=(
...     'Mountain View,VA94040',
...     'Sunnyvale, VA',
...     'Los Altos, 94023',
...     'Cupertino 95014',
...     'Palo Alto CA',
... )
>>> for datum in DATA:
...     print re.split(', |(?= (?:\d{5}|[A-Z]{2})) ',datum)
...
['Mountain View', 'VA', '94040']
['Sunnyvale', 'VA']
['Los Altos', '94023']
['Cupertino', '95014']
['Palo Alto', 'CA']

扩展符号

re.M/re.MULTILINE实现多行混合

>>> re.findall(r'(?i)yes','yes? Yes. YES!!')
['yes', 'Yes', 'YES']
>>> re.findall(r'(?i)th\w+','The quickest way is through this tunnel.')
['The', 'through', 'this']
>>> re.findall(r'(?im)(^th[\w ]+)',"""
... This line is the first,
... another line,
... that line, it's the best
... """)
['This line is the first', 'that line']

re.S/re.DOTALL使点号能够用来表示\n符号

>>> re.findall(r'th.+','''
... The first line
... the second line
... the third line
... ''')
['the second line', 'the third line']
>>> re.findall(r'(?s)th.+','''
... The first line
... the second line
... the third line
... ''')
['the second line\nthe third line\n']

re.X/VERBOSE

该标记允许用户通过抑制在正则表达式中使用空白符(除了在字符类中或者在反斜线转义中)来创建更易读的正则表达式。

>>> re.search(r'''(?x)
...     \((\d{3})\)  #区号
...     [ ]          #空白符
...     (\d{3})      #前缀
...     -            #横线
...     (\d{4})      #终点数字
... ''','(800) 555-1212').groups()
('800', '555', '1212')

(?:...)对正则表达式进行分组

(?:...)可以对部分正则表达式进行分组,但是并不会保存该分组。

>>> re.findall(r'http://(?:\w+\.)*(\w+\.com)','http://google.com http://www.google.com http://code.google.com')
['google.com', 'google.com', 'google.com']
>>> re.search(r'\((?P\d{3})\) (?P\d{3})-(?:\d{4})','(800) 555-1212').groupdict()
{'areacode': '800', 'prefix': '555'}

(?P< name >)

(?P< name >) 通过使用一个名称标识符而不是使用从1开始增加到N的增量数字来保存匹配,可以使用一个类似风格的\g< name >来检索它们。

>>> re.sub(r'\((?P\d{3})\) (?P\d{3})-(?:\d{4})','(\g) \g-xxxx','(800) 555-1212')
'(800) 555-xxxx'

(?P=name)

可以在一个相同的正则表达式中重用模式,而不必稍后再次在(相同)正则表达式中指定相同的模式。

>>> bool(re.match(r'\((?P\d{3})\) (?P\d{3})-(?P\d{4}) (?P=areacode)-(?P=prefix)-(?P=number) 1(?P=areacode)(?P=prefix)(?P=number)','(800) 555-1212 800-555-1212 18005551212'))
True
>>> bool(re.match(r'''(?x)
...     \((?P\d{3})\)[ ](?P\d{3})-(?P\d{4})
...     [ ]
...     (?P=areacode)-(?P=prefix)-(?P=number)
...     [ ]
...      1(?P=areacode)(?P=prefix)(?P=number)
... ''', '(800) 555-1212 800-555-1212 18005551212'))
True

正向前视断言(?...) 负向前视断言 (?!...)

>>> re.findall(r'\w+(?= van Rossum)',
... '''
...     Gui van Rossum
...     Tim Peters
...     Alex Martrlli
...     Just van Rossum
...     Raymond Hettinger
... ''')
['Gui', 'Just']
>>> re.findall(r'(?m)^\s+(?!noreply|postmaster)(\w+)', 
... '''
...     [email protected]
...     [email protected]
...     [email protected]
...     [email protected]
...     [email protected]
... ''')
['sales', 'eng', 'admin']
>>> ['%[email protected]' %e.group(1) for e in re.finditer(r'(?m)^\s+(?!noreply|postmaster)(\w+)',
... '''
...     [email protected]
...     [email protected]
...     [email protected]
...     [email protected]
...     [email protected]
... ''')]
['[email protected]', '[email protected]', '[email protected]']

使用条件正则表达式匹配

两字母的字符串必须由一个字母跟着另一个字母,不能同时拥有两个相同的字母。

>>> bool(re.search(r'(?:(x)|y)(?(1)y|x)','xy'))
True
>>> bool(re.search(r'(?:(x)|y)(?(1)y|x)','xx'))
False

使用python原始字符串

退格符\b和正则表达式\b之间的差异

>>> m=re.match('\bblow','blow')
>>> if m:    m.group()
...
>>> m=re.match('\\bblow','blow')
>>> if m:    m.group()
...
'blow'
>>> m=re.match(r'\bblow','blow')
>>> if m:    m.group()
...
'blow'

一些正则表达式示例

示例1-1 分割windows的tasklist命令 file对象

部分cmd命令——tasklist展示:
映像名称                       PID 会话名              会话#       内存使用
========================= ======== ================ =========== ============
System Idle Process              0 Services                   0          4 K
System                           4 Services                   0      9,212 K
smss.exe                       464 Services                   0        544 K
csrss.exe                      708 Services                   0      2,792 K
wininit.exe                    816 Services                   0      2,808 K
services.exe                   888 Services                   0      5,220 K
lsass.exe                      904 Services                   0     10,984 K
import os
import re
f = os.popen('tasklist', 'r')
for eachLine in f:
    print re.split(r'\s\s+|\t', eachLine.rstrip()) #使用str.rstrip()去除尾部的\n
f.close()
部分python结果展示:
['\xd3\xb3\xcf\xf1\xc3\xfb\xb3\xc6', 'PID \xbb\xe1\xbb\xb0\xc3\xfb', '\xbb\xe1\xbb\xb0#', '\xc4\xda\xb4\xe6\xca\xb9\xd3\xc3']
['========================= ======== ================ =========== ============']
['System Idle Process', '0 Services', '0', '4 K']
['System', '4 Services', '0', '9,212 K']
['smss.exe', '464 Services', '0', '544 K']
['csrss.exe', '708 Services', '0', '2,796 K']
['wininit.exe', '816 Services', '0', '2,808 K']
['services.exe', '888 Services', '0', '5,220 K']
['lsass.exe', '904 Services', '0', '10,968 K']

示例1-2 分割windows的tasklist命令 with语句

当使用with语句时,file对象的上下文管理器会自动调用f.close()

import os
import re
with os.popen('tasklist', 'r') as f:
    for eachLine in f:
        print re.split(r'\s\s+|\t', eachLine.rstrip())#使用str.rstrip()去除尾部的\n

部分python结果展示:
['\xd3\xb3\xcf\xf1\xc3\xfb\xb3\xc6', 'PID \xbb\xe1\xbb\xb0\xc3\xfb', '\xbb\xe1\xbb\xb0#', '\xc4\xda\xb4\xe6\xca\xb9\xd3\xc3']
['========================= ======== ================ =========== ============']
['System Idle Process', '0 Services', '0', '4 K']
['System', '4 Services', '0', '9,212 K']
['smss.exe', '464 Services', '0', '544 K']
['csrss.exe', '708 Services', '0', '2,796 K']
['wininit.exe', '816 Services', '0', '2,808 K']
['services.exe', '888 Services', '0', '5,220 K']
['lsass.exe', '904 Services', '0', '10,968 K']

示例1-3 分割windows的tasklist命令 printf语句(2、3通用)

distutils.log.warn()是一个单字符串输出函数,因此如果输出要复杂一些们就需要合并所有输出到一个字符串中,然后调用。

import os
from distutils.log import warn as printf
import re
with os.popen('tasklist', 'r') as f:
    for eachLine in f:
        printf(re.split(r'\s\s+|\t', eachLine.rstrip()))
部分python结果展示:
['\xd3\xb3\xcf\xf1\xc3\xfb\xb3\xc6', 'PID \xbb\xe1\xbb\xb0\xc3\xfb', '\xbb\xe1\xbb\xb0#', '\xc4\xda\xb4\xe6\xca\xb9\xd3\xc3']
['========================= ======== ================ =========== ============']
['System Idle Process', '0 Services', '0', '4 K']
['System', '4 Services', '0', '9,212 K']
['smss.exe', '464 Services', '0', '544 K']
['csrss.exe', '708 Services', '0', '2,796 K']
['wininit.exe', '816 Services', '0', '2,808 K']
['services.exe', '888 Services', '0', '5,220 K']
['lsass.exe', '904 Services', '0', '10,968 K']

示例1-4 处理DOS环境下tasklist命令的输出

import os
import re
f = os.popen('tasklist /nh', 'r')
for eachLine in f:
    print re.findall(r'([\w.]+(?: [\w.]+)*)\s\s+(\d+) \w+\s\s+\d+\s\s+([\d,]+ K)', eachLine.rstrip())
f.close()
部分cmd命令——tasklist /nh展示:
System Idle Process              0 Services                   0          4 K
System                           4 Services                   0      9,228 K
smss.exe                       464 Services                   0        544 K
csrss.exe                      708 Services                   0      2,800 K
wininit.exe                    816 Services                   0      2,804 K
services.exe                   888 Services                   0      5,144 K

部分python结果展示:(已截断)
[]
[('System Idle Process', '0', '4 K')]
[('System', '4', '9,228 K')]
[('smss.exe', '464', '544 K')]
[('csrss.exe', '708', '2,800 K')]
[('wininit.exe', '816', '2,804 K')]
[('services.exe', '888', '5,088 K')]

更长的正则表达式示例

示例1-5 用于正则表达式练习的数据生成器

该脚本为正则表达式练习创建随机数据,然后将生成的数据输出到屏幕。

from random import randrange, choice
from string import ascii_lowercase as lc # string.ascii_lowercase是字母表中拥有26个小写字母的序列集合
from sys import maxint
from time import ctime

tlds = ('com', 'edu', 'net', 'org', 'gov')  # 高级域名集合

for i in xrange(randrange(5, 11)):
    dtint = randrange(maxint)  # 随机生成一个整数
    dtstr = ctime(dtint)       # 使用time.ctime()函数将该整数转换为日期
    llen = randrange(4, 8)     # login is shorter
    login = ''.join(choice(lc) for j in range(llen)) # random.choice()函数的功能是接受一个序列,然后返回该序列的一个随机元素
    dlen = randrange(llen, 13)  # domain is longer
    dom = ''.join(choice(lc) for j in xrange(dlen))
    print '%s::%s@%s.%s::%d-%d-%d' % (dtstr, login, dom, choice(tlds), dtint, llen, dlen)
输出结果:
Wed Mar 20 13:40:35 2002::[email protected]::1016602835-4-6
Sun Feb 21 00:44:33 1993::[email protected]::730226673-7-9
Mon Feb 11 04:03:20 2008::[email protected]::1202673800-6-11
Fri May 05 14:00:38 1978::[email protected]::263196038-4-9
Thu Dec 26 16:38:22 2024::[email protected]::1735202302-4-5
Sun Oct 28 08:02:14 1990::[email protected]::657072134-6-6
Thu Dec 30 01:30:21 2010::[email protected]::1293643821-7-11
Sun Apr 27 01:53:57 2014::[email protected]::1398534837-4-10
Tue Oct 02 16:26:34 1979::[email protected]::307700794-4-5

匹配字符串

>>> import re
>>> data='Wed Mar 20 13:40:35 2002::[email protected]::1016602835-4-6'
>>> patt='^(Mon|Tue|Wed|Thu|Fri|Sat|Sun)'
>>> m=re.match(patt,data)
>>> m.group()
'Wed'
>>> m.group(1)
'Wed'
>>> m.groups()
('Wed',)

分组 ^()

>>> patt='^(\w{3})'
>>> m=re.match(patt,data)
>>> if m is not None:    m.group()
...
'Wed'
>>> m.group(1)
'Wed'
>>> patt='^(\w){3}'
>>> m=re.match(patt,data)
>>> if m is not None:    m.group()
...
'Wed'
>>> m.group(1) # 子组1持续被下一个字符替换
'd'

搜索、匹配与贪婪

>>> patt='\d+-\d+-\d+'
>>> re.search(patt,data).group()
'1016602835-4-6'
>>> patt='.+(\d+-\d+-\d+)'  # +是一个贪婪的操作符
>>> re.match(patt,data).group()
'Wed Mar 20 13:40:35 2002::[email protected]::1016602835-4-6'
>>> re.match(patt,data).group(1)  # 贪婪匹配
'5-4-6

>>> patt='.+?(\d+-\d+-\d+)'  # ?是一个非贪婪操作符
>>> re.match(patt,data).group(1)
'1016602835-4-6

你可能感兴趣的:(python核心编程第3版第1章 正则表达式【读书笔记】)