概述
1. 正则表达式基础、
简单介绍:
正则表达式是搜索替换和解析复杂字符模式的一种强大而标准的方法,正则表达式并不是Python的一部分,他拥有自己的独特语法和独立的处理引擎,效率上可能不如str自带方法,但是功能十分强大。入门:
正则表达式的大致匹配过程是:依次拿出表达式和文本中的字符比较,如果每一个字符都能匹配,则匹配成功;一旦有匹配不成功的字符则匹配失败。如果表达式中有量词或边界,这个过程会稍微有一些不同,但也是很好理解的。
下图列出了Python支持的正则表达式元字符和语法:
出了不少简单的例子,并对它们作了详细的说明。
1)、假设你在一篇英文小说里查找 hi ,你可以使用正则表达式 hi 。这几乎是最简单的正则表达式了,它可以精确匹配这样的字符串: 由两个字符组成,前一个字符是h,后一个是i 。但是很多单词里包含 hi 这两个连续的字符,比如 him , history等。用 hi 来查找的话,这里边的 hi 也会被找出来。如果要 精确地查找hi这个单词 的话,我们应该使用 bhi b 。 b 是正则表达式规定的一个特殊代码(好吧,某些人叫它 元字符,metacharacter ),代表着单词的开头或结尾,也就是单词的分界处 。虽然通常英文的单词是由空格,标点符号或者换行来分隔的,但是 b 并不匹配这些单词分隔字符中的任何一个,它只匹配一个位置。假如你要找的是 hi后面不远处跟着一个Lucy ,你应该用
此, .* 连在一起就意味着任意数量的不包含换行的字符 。 现在 bhi b.* bLucy b 的意思就很明显了: 先 是一个单词hi ,然后是任意个任意字符(但不能是换行),最后是Lucy这个单词 。
2) d+ 匹配 1个或更多连续的数字 。这里的 + 是和 * 类似的元字符,不同的是 * 匹配 重复任意次(可能
是0次) ,而 + 则匹配 重复1次或更多次 。
3)、 bw{6} b 匹配 刚好6个字符的单词
2)、<a[^>]+> 匹配用尖括号括起来的以a开头的字符串 。
如果想查找元字符本身的话,比如你查找 . ,或者 * ,就出现了问题:你没办法指定它们,因为它们会被解释成别的意思。这时你就得使用 来取消这些字符的特殊意义。因此,你应该使用 . 和 * 。当然,查找 本身,得用
下面是一个更复杂的表达式: (?0 d{2}[)-]? d{8} 。这个表达式可以匹配 几种格式的电话号码 ,像(010)88886666 ,或 022-22334455 ,或 02912345678 等。
1)、0 d{2}- d{8}|0 d{3}- d{7} 这个表达式能 匹配两种以连字号分隔的数字。
2)、 (?0 d{2} )?[-]? d{8}|0 d{2}[-]? d{8} 这个表达式 匹配3位区号的电话号码,其中区号可以用小括号括起来,也可以不用,区号与本地号间可以用连字号或空格间隔,也可以没有间隔 。
3)、 d{5}- d{4}| d{5} 这个表达式用于匹配美国的邮政编码。美国邮编的规则是5位数字,或者用连字号间隔的9位数字。之所以要给出这个例子是因为它能说明一个问题:使用分枝条件时,要注意各个条件的顺序。如果你把它改成 d{5}| d{5}- d{4} 的话,那么就只会匹配5位的邮编(以及9位邮编的前5位)。原因是匹配分枝条件时,将会从左到右地测试每个条件,如果满足了某个分枝的话,就不会去再管其它的条件了。
( d{1,3} .){3} d{1,3} 是一个 简单的IP地址匹配 表达式。 d{1,3} 匹配 1到3位的数字 , ( d{1,3} .){3} 匹配 三位数字加上一个英文句号(这个整体也就是这个分组)重复3次 ,最后再加上 一个一到三位的数字 ( d{1,3} )。 不幸的是,它也将匹配 256.300.888.999 这种不可能 存在的IP地址。如果能使用算术比较的话,或许能简单 地解决这个问题,但是正则表达式中并不提供关于数学 的任何功能,所以只能使用冗长的分组,选择,字符类 来描述一个正确的IP地址: ((2[04] d|25[05]|[01]? d d?) .){3}(2[04] d|25[05]|[01]? d d?) 。
的部分) ,如查找 I' m si ngi ng whi l e you' re danci ng. 时,它会匹配 si ng 和 danc 。
(?<=exp) 也叫 零宽度正回顾后发断言 ,它 断言自身出现的位置的前面能匹配表达式exp 。
假如你想要给一个很长的数字中每三位间加一个逗号(当然是从右边加起了),你可以这样查找需要
在前面和里面添加逗号的部分: ((?<= d) d{3})+ b ,用它对 1234567890 进行查找时结果是 234567890 。
下面这个例子同时使用了这两种断言: (?<= s) d+(?= s) 匹配 以空白符间隔的数字(再次强调,不包
括这些空白符) 。
前面我们提到过怎么查找不是某个字符或不在某个字符类里的字符的方法(反义)。但是如果我们只是想要确保某个字符没有出现,但并不想去匹配它时怎么办?
bw*q[^u]w* b 匹配 包含后面不是字母u的字母q的单词 。但是如果多做测试(或者你思维足够敏锐,直接就观察出来了),你会发现,如果q出现在单词的结尾的话,像Iraq,Benq,这个表达式就会出错。这是因为 [^u] 总要匹配一个字符,所以如果q是单词的最后一个字符的话,后面的 [^u] 将会匹配q后面的单词分隔符(可能是空格,或者是句号或其它的什么),后面的 w* b 将会匹配下一个单词,于是 bw*q[^u]w* b 就能匹配整个 Iraq f i ght i ng 。 负向零宽断言 能解决这样的问题,因为它只匹配一个位置,并不消费任何字符。现在,我们可以这样来解决这个问题:
零宽度负预测先行断言 (?!exp) , 断言此位置的后面不能匹配表达式exp 。
< ( w + ) > # 查找尖括号括起来的字母或数字( 即H T M L / X M L 标签)
) # 前缀结束
. * # 匹配任意文本
( ? = # 断言要匹配的文本的后缀
< / 1 > # 查找尖括号括起来的内容:前面是一个" / " ,后面是先前捕获的标签
) # 后缀结束
有时,我们更需要 懒惰 匹配,也就是匹配尽可能少的字符。前面给出的限定符都可以被转化为懒惰匹配模式,只要在它后面加上一个问号 ?。这样 .*? 就意味着 匹配任意数量的重复,但是在能使整个匹配成功的前提下使用最少的重复 。
a.*?b 匹配 最短的,以a开始,以b结束的字符串 。如果把它应用于 aabab 的话,它会匹配 aab(第一到第三个字符) 和 ab(第四到第五个字符) 。
注: 你可能发问了:既然是懒惰匹配,为什么第一个匹配是aab(第一到第三个字符)而不是ab(第二到第三个字符)?简单地说,因为正则表达式有另一条规则,且比懒惰 / 贪婪规则的优先级更高:最先开始的匹配拥有最高的优先权。
最左边的左括号和最右边的右括号之间的内容(这里我们讨论的是贪婪模式,懒惰模式也有下面的问题)。假
如原来的字符串里的左括号和右括号出现的次数不相等,比如 ( 5 / ( 3 + 2 ) ) ) ,那我们的匹配结果里两者的个数也不会相等。有没有办法在这样的字符串里匹配到最长的,配对的括号之间的内容呢?
为了避免 ( 和 ( 把你的大脑彻底搞糊涂,我们还是用尖括号代替圆括号吧。现在我们的问题变成了如何把
这里需要用到以下的语法构造:
(?'group') 把捕获的内容命名为group,并压入 堆栈(Stack)
(?'-group') 从堆栈上弹出最后压入堆栈的名为group的捕获内容,如果堆栈本来为空,则本分组的匹配失败
(?(group)yes|no) 如果堆栈上存在以名为group的捕获内容的话,继续匹配yes部分的表达式,否则继续匹配no部分
(?!) 零宽负向先行断言,由于没有后缀表达式,试图匹配总是失败
我们需要做的是每碰到了左括号,就在压入一个"Open",每碰到一个右括号,就弹出一个,到了最后就看看堆栈是否为空--如果不为空那就证明左括号比右括号多,那匹配就应该失败。正则表达式引擎会进行回溯(放弃最前面或最后面的一些字符),尽量使整个表达式得到匹配。
< # 最外层的左括号
[ ^ < > ] * # 最外层的左括号后面的不是括号的内容
(
(
( ? ' O p e n ' < ) # 碰到了左括号,在黑板上写一个" O p e n "
[ ^ < > ] * # 匹配左括号后面的不是括号的内容
) +
(
( ? ' - O p e n ' > ) # 碰到了右括号,擦掉一个" O p e n "
[ ^ < > ] * # 匹配右括号后面不是括号的内容
) +
) *
( ? ( O p e n ) ( ? ! ) ) # 在遇到最外层的右括号前面,判断黑板上还有没有没擦掉的" O p e n " ;如果 还有,则匹配失败
> # 最外层的右括号
平衡组的一个最常见的应用就是匹配HTML,下面这个例子可以匹配 嵌套的<di v>标签 :
简单且容易阅读,而快速、简单、可读性强的代码可以说出很多好处。但是,
如果你发现你使用了许多不同的字符串函数和 if 语句来处理一个特殊情况,
或者你组合使用了 split、join 等函数而导致用一种奇怪的甚至读不下去的方式
理解列表,此时,你也许需要转到正则表达式了。
<span style="font-size:18px;">>>> S = "100 north main road"
>>> S.replace('road' , 'rd')
#使用字符串方法
'100 north main rd'
>>> S = "100 north broad road"
#这里使用字符串方法出现问题,我们的目的是替换road,但是broad
中的子字符串也被替换掉
>>> S.replace('road' , 'rd')
'100 north brd rd'
>>> S[:-4]+S[-4:].replace('road' , 'rd.')
#使用字符串中的索引和分片方法解决,但是代码可读性差
'100 north broad rd.'
>>> import re
#使用正则表达式,re为Python为支持正则表达式提供的模块
>>> re.sub('road$','rd',S)
#这个正则表达式十分简单,road$:只有road出现在一个字符串尾部时才匹配,$表示“字符串的末尾”,利用re.sub对字符串进行搜索,满足正则表达式road$的用rd替换
'100 north broad rd'</span>
<span style="font-size:18px;"></span><span style="font-family: monospace; white-space: pre; "><span style="font-size:18px;">>>> S = '100BROAD'</span></span>
<span style="font-size:18px;"><span style="font-family: monospace; white-space: pre; "></span>>>> re.sub('ROAD$','RD',S)
#只有当ROAD出现在字符串末尾时替换
'100BRD'
>>> re.sub('\bROAD$','RD',S) #只有当ROAD出现在字符串尾部并且作为一个独立的单词时替换,b表示单词的边界,第一个为转移字符</span>
<span style="font-size:18px;">
>>> re.sub(r'bROAD$','RD',S) #这是上面的改良版,使用了Python的Raw字符
'100BROAD'</span></span>
<span style="font-size:18px;">>>> S = '100BROAD200'
>>> re.sub(r'bROADb','RD',S)
#当ROAD不在末尾时,上面的匹配就会失败,所以在这里给ROAD的前后都加上b(单词边界),这样就能匹配整个字符串中的所有的ROAD了。神奇有木有
'100BROAD200'
>>> S = '100 BROAD 200'
>>> re.sub(r'bROADb','RD',S)
'100 BROAD 200'
>>> S = '100 BROAD ROAD
200'
>>> re.sub(r'bROADb','RD',S)
'100 BROAD RD
200'</span>
一些Python 的RE方法及其应用(https://docs.python.org/2/library/re.html)
<span style="font-size:18px;">>>> import re
>>> help (re)
This module exports the following functions:</span>
<span style="font-size:18px;"> match Match a regular expression pattern to the beginning of a string.
search Search a string for the presence of a pattern.
sub Substitute occurrences of a pattern found in a string.
subn Same as sub, but also return the number of substitutions made.
split Split a string by the occurrences of a pattern.
findall Find all occurrences of a pattern in a string.
finditer Return an iterator yielding a match object for each match.
compile Compile a pattern into a RegexObject.
purge Clear the regular expression cache.
escape Backslash all non-alphanumerics in a string.
Some of the functions in this module takes flags as optional parameters:
I IGNORECASE Perform case-insensitive matching.
L LOCALE Make w, W, b, B, dependent on the current locale.
M MULTILINE "^" matches the beginning of lines (after a newline)
as well as the string.
"$" matches the end of lines (before a newline) as well
as the end of the string.
S DOTALL "." matches any character at all, including the newline.
X VERBOSE Ignore whitespace and comments for nicer looking RE's.
U UNICODE Make w, W, b, B, dependent on the Unicode locale.
</span>
<span style="font-size:18px;">>>> help (re.match)
这样查看每个方法的帮助文档, 可以看到每一种方法的参数
match(pattern, string, flags=0)
Try to apply the pattern at the start of the string, returning
a match object, or None if no match was found.
(END) </span>
<span style="font-size:18px;">>>> pattern = re.compile(r'hello') # 将正则表达式编译成Pattern对象
>>> match = pattern.match('hello world!') #使用Pattern匹配文本,获得匹配结果,无法匹配时将返回None
>>> if match:
...
print match.group()
#获得一个或多个分组截获的字符串;</span></span><span style="font-size:18px;">
...
hello</span>
re.compile(strPattern[, flag]):
这个方法是Pattern类的工厂方法,用于将字符串形式的正则表达式编译为Pattern对象。 第二个参数flag是匹配模式,取值可以使用按位或运算符'|'表示同时生效,比如re.I | re.M。另外,你也可以在regex字符串中指定模式,比如re.compile('pattern', re.I | re.M)与re.compile('(?im)pattern')是等价的。
可选值有:
名称 | 修饰符 | 说明 |
IGNORECASE(忽略大小写) | re.I re.IGNORECASE | 忽略大小写,使匹配对大小写不敏感 |
MULTILINE (多行模式) | re.M re.MULTILINE | 多行模式,改变'^'和'$'的行为 |
DOTALL(点任意匹配模式) | re.S re.DOTALL | 点任意匹配模式,改变'.'的行为,使 '.' 匹配包括换行在内的所有字符; 没有这个标志, "." 匹配除了换行外的任何字符。 |
LOCALE(使预定字符类) | re.L re.LOCALE | 做本地化识别(locale-aware)匹配,使预定字符类 w W b B s S 取决于当前区域设定。 locales 是 C 语言库中的一项功能,是用来为需要考虑不同语言的编程提供帮助的。 举个例子,如果你正在处理法文文本,你想用 "w+ 来匹配文字,但 "w 只匹配字符类 [A-Za-z];它并不能匹配 "é" 或 "?"。如果你的系统配置适当且本地化设置为法语, 那么内部的 C 函数将告诉程序 "é" 也应该被认为是一个字母。 |
UNICODE(Unicode模式) | re.U re.UNICODE | 根据Unicode字符集解析字符,使预定字符类 w W b B s S d D 取决于unicode定义的字符属性 |
VERBOSE(详细模式) | re.X re.VERBOSE | 这个模式下正则表达式可以是多行,忽略空白字符,并可以加入注释。详见4.1 松散正则表达式 |
<span style="font-size:18px;">>>> m = re.match(r'hello','hello world')
>>> print m.group()
hello
</span>
2)、Match
<span style="font-size:18px;">>>> help(re.match)
Help on function match in module re:
match(pattern, string, flags=0)
Try to apply the pattern at the start of the string, returning
a match object, or None if no match was found.</span>
match()函数只检测pattern是不是在string的开始位置匹配, 也就是说match()只有在0位置匹配成功的话才有返回,如果不是开始位置匹配成功的话,match()就返回none。
eg:
<span style="font-size:18px;">>>> print re.match(r'hello','hello world').group()
hello
>>> print re.match(r'hello','Python hello world').group()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'group'</span>
Match对象是一次匹配的结果,包含了很多关于此次匹配的信息,可以使用Match提供的可读属性或方法来获取这些信息。
属性:
- string: 匹配时使用的文本。
- re: 匹配时使用的Pattern对象。
- pos: 文本中正则表达式开始搜索的索引。值与Pattern.match()和Pattern.seach()方法的同名参数相同。
- endpos: 文本中正则表达式结束搜索的索引。值与Pattern.match()和Pattern.seach()方法的同名参数相同。
- lastindex: 最后一个被捕获的分组在文本中的索引。如果没有被捕获的分组,将为None。
- lastgroup: 最后一个被捕获的分组的别名。如果这个分组没有别名或者没有被捕获的分组,将为None。
方法:
- group([group1, …]):
获得一个或多个分组截获的字符串;指定多个参数时将以元组形式返回。group1可以使用编号也可以使用别名;编号0代表整个匹配的子串;不填写参数时,返回group(0);没有截获字符串的组返回None;截获了多次的组返回最后一次截获的子串。 - groups([default]):
以元组形式返回全部分组截获的字符串。相当于调用group(1,2,…last)。default表示没有截获字符串的组以这个值替代,默认为None。 - groupdict([default]):
返回以有别名的组的别名为键、以该组截获的子串为值的字典,没有别名的组不包含在内。default含义同上。 - start([group]):
返回指定的组截获的子串在string中的起始索引(子串第一个字符的索引)。group默认值为0。 - end([group]):
返回指定的组截获的子串在string中的结束索引(子串最后一个字符的索引+1)。group默认值为0。 - span([group]):
返回(start(group), end(group))。 - expand(template):
将匹配到的分组代入template中然后返回。template中可以使用id或g<id>、g<name>引用分组,但不能使用编号0。id与g<id>是等价的;但10将被认为是第10个分组,如果你想表达1之后是字符'0',只能使用g<1>0。
<span style="font-size:18px;">>>> m = re.match(r'python','python hello')
>>> print m.group()
#获取分组截获的字符串
python
>>> print m.string
#获取匹配时使用的文本
python hello
>>> print m.re
#匹配时使用的Pattern对象(先将正则表达式的字符串形式编译为Pattern实例,
然后使用Pattern实例处理文本并获得匹配结果)
<_sre.SRE_Pattern object at 0xb7458bd0>
>>> print m.pos
#文本中正则表达式开始搜索的索引</span>
0
>>> print m.endpos
#文本中正则表达式结束搜索的索引,与len(string)相同</span>
12
>>> print m.lastindex
#最后一个被捕获的分组在文本中的索引。如果没有被捕获的分组,将为None</span>
None
>>> print m.lastgroup
#最后一个被捕获的分组的别名。如果这个分组没有别名或者没有被捕获的分组,将
为None
None
>>> print m.groups()
以元组形式返回全部分组截获的字符串</span>
()
>>> print m.groupdict()
{}
>>> print m.start()
0
>>> print m.end()
6
>>> print m.span()
(0, 6)</span>
3)、 Pattern
Pattern对象是一个编译好的正则表达式,通过Pattern提供的一系列方法可以对文本进行匹配查找。
Pattern不能直接实例化,必须使用re.compile()进行构造。
Pattern提供了几个可读属性用于获取表达式的相关信息:
- pattern: 编译时用的表达式字符串。
- flags: 编译时用的匹配模式。数字形式。
- groups: 表达式中分组的数量。
- groupindex: 以表达式中有别名的组的别名为键、以该组对应的编号为值的字典,没有别名的组不包含在内。
match VS search: re.match 从字符串的开始处开始匹配,如果字符串开始不符合正则表达式,则匹配失败,函数返回None;而re.search 查找整个字符串,直到找到一个匹配;若无匹配返回None。
<span style="font-size:18px;">>>> print re.match(r'hello','hello world').group()
hello
>>> print re.match(r'hello','python hello world').group()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'group'
>>> print re.search(r'hello','python hello world').group()
hello
</span>
<span style="font-size:18px;">>>> p = re.compile(r'(w+) (w+)')
#‘(w+) (w+)’空格连接的两个单词
>>> s = 'i say, hello world!'
>>> print p.sub(r'2 1', s)
#以id引用分组,将每个匹配中的两个单词掉转位置
say i, world hello!
>>> def func(m):
#将匹配单词的首字母改为大写
...
return m.group(1).title() + ' ' + m.group(2).title()
...
>>> print p.sub(func, s)
I Say, Hello World!</span>
subn
subn(pattern, repl, string, count=0, flags=0):此函数的参数与 sub 函数完全一致,只不过其返回值是包括两个元素的元组:(new_string, number);第一个返回值 new_string 为sub 函数的结果,第二个 number 为匹配及替换的次数。
<span style="font-size:18px;">>>> import re
>>> p = re.compile(r'(w+) (w+)')
>>> s = 'i say, hello world!'
>>> print p.subn(r'2 1', s)
('say i, world hello!', 2)
>>> def func(m):
...
return m.group(1).title() + ' ' + m.group(2).title()
...
>>> print p.subn(func, s)
('I Say, Hello World!', 2)</span>
4)、findall
<span style="font-size:18px;">>>> p = re.compile(r'bwb')
>>> print p.findall('one 1 two 2 three 3 four 4')
['1', '2', '3', '4']
>>> p = re.compile(r'bw+b')
>>> print p.findall('one 1 two 2 three 3 four 4')
['one', '1', 'two', '2', 'three', '3', 'four', '4']</span>
5)、finditer
finditer(pattern, string, flags=0):找到 RE 匹配的所有子串,并把它们作为一个迭代器返回。这个匹配是从左到右有序地返回。如果无匹配,返回空列表。
<span style="font-size:18px;">>>> p = re.compile(r'bw+b')
>>> for m in p.finditer('one1 two2 three3 four4'):
...
print m.group()
...
one1
two2
three3
four4</span>
6)、purge
purge():清空缓存中的正则表达式。
<span style="font-size:18px;">>>> p = re.compile(r'bw+b')
>>> p.search('one1 two2 three3 four4').group()
'one1'
>>> p = re.purge()
#清空缓存中的正则表达式
>>> p.search('one1 two2 three3 four4').group()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'search'
</span>
7)、split
照旧,我们使用help:
split(pattern, string, maxsplit=0, flags=0):按照能够匹配的子串将string分割后返回列表。maxsplit用于指定最大分割次数,不指定使用默认值0将全部分割。如设定好分割次数后,最后未分割部分将作为列表中的一个元素返回。<span style="font-size:18px;">>>> p = re.compile(r' ')
>>> print p.split('one1 two2 three3 four4')
['one1', 'two2', 'three3', 'four4']
</span>
上述内容仅作学习记录!!!!!!
https://docs.python.org/2/library/re.html
最后
以上就是干净天空为你收集整理的正则表达式——python(学习记录) 一些Python 的RE方法及其应用(https://docs.python.org/2/library/re.html) https://docs.python.org/2/library/re.html的全部内容,希望文章能够帮你解决正则表达式——python(学习记录) 一些Python 的RE方法及其应用(https://docs.python.org/2/library/re.html) https://docs.python.org/2/library/re.html所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复