拓展:正则表达式-常用函数
该部分为中谷教育Python视频教程的学习笔记(内容较多)
使用正则表达式
re模块提供了一个正则表达式引擎的接口,可以让你将REstring编译成对象并用它们来进行匹配
编译正则表达式:
>>> import re >>> p = re.compile('ab*') >>> print(p) <_sre.SRE_Pattern object at 0x02425D30> >>>
如果说有一段正则表达式要经常用来匹配的话,那么建议把正则表达式编译出来。编译方式就是re的方法,如re.compile,re.match等
>>> r1 = r'\d{3,4}-?\d{8}' #匹配一个电话号码。 #\d的意思是匹配任何十进制数字也就是0-9,{3,4}是重复3次或者4次, #如区号,有的是3个比如010,020,有的是4个比如0753,0760,-?表示这个-号可有可无 #后面的\d{8}是指匹配的十进制数字重复8次 >>> re.findall(r1,'010-12345678') ['010-12345678'] >>> re.findall(r1,'0760-12888678') ['0760-12888678'] >>>
编译方式
re.compile可以把正则表达式编译成一个正则表达式对象。
可以把那些经常使用的正则表达式编译成正则表达式对象,这样可以提高一定的效率。
>>> p_tel = re.compile(r1) #取一个比较容易识别的名字,如tel就知道是在匹配电话号码 >>> p_tel <_sre.SRE_Pattern object at 0x0250E3C0> #返回一个正则表达式的对象 >>> p_tel.findall('010-12345678') #编译之后的正则比未编译的正则运算速度要快很多。 ['010-12345678'] >>>
re.compile()也接受可选的标志参数,常用来实现不同的特殊功能和语法变更
如现在写一个正则,在匹配当中不区分大小写。
>>> fishdm_re = re.compile(r'fishdm', re.I) #后面加一个属性,re.I >>> fishdm_re.findall('fishdm') ['fishdm'] >>> fishdm_re.findall('FISHDM') ['FISHDM'] >>> fishdm_re.findall('FiShDm') ['FiShDm'] >>>
执行匹配
RegexObjet实例有一些方法和属性,完整的列表可以查看 Python参考库
也可参考网文:http://www.cnblogs.com/sevenyuan/archive/2010/12/06/1898075.html
match() 决定re是否在字符串刚开始的位置匹配
search() 扫描字符串,找到这个re匹配的位置
如果没有匹配到的话,match()和search()将返回None,如果成功的话,就会返回一个“MatchObject”实例。
>>> fishdm_re = re.compile(r'fishdm', re.I) >>> fishdm_re.match('fishdm hello!') <_sre.SRE_Match object at 0x02277608> #返回一个对象,包含这个fishdm字符串的 >>> fishdm_re.match('hello') >>> #返回一个None >>> fishdm_re.match('hello fishdm') >>> #如果把fishdm放在后面,返回也是空
# 用match去匹配字符串的话,只有被匹配元素或者数据在字符串开始的位置才会返回一个对象 #通常我们利用match的特性来判断匹配数据是否成功 >>> x = fishdm_re.match('fishdm hello') >>> if x: print('匹配成功')
search() 与 match()的区别 >>> fishdm_re = re.compile(r'fishdm', re.I) >>> x = fishdm_re.search('hello fishdm hao') >>> if x: print('匹配成功') 匹配成功 >>>
findall()找到re匹配的所有子串,并把它们作为一个列表返回
>>> txt = 'fishdm study hard, he is sure to succeed' >>> fishdm_re = re.compile(r'fishdm', re.I) >>> fishdm_re.findall(txt) ['fishdm'] >>> fishdm_re = re.compile(r'su', re.I) >>> fishdm_re.findall(txt) ['su', 'su'] >>>
finditer()找到re匹配的所有子串,并把它们作为一个迭代器返回
MatchObject实例方法:
m.group()返回被re匹配的字符串
m.start()返回匹配开始的位置
m.end()返回匹配结束的位置
m.span()返回一个元组包含匹配(开始,结束)的位置
在实际程序中,最常见的做法是将“MatchObject”保存在一个变量里,然后检查它是否为None
>>> fishdm_re = re.compile(r'fishdm', re.I) >>> x = fishdm_re.search('fishdm hao') >>> if x: print('匹配成功')
模块级函数:
re模块也提供了顶级函数调用
如: match(),search(),sub(),subn(),spilt(),findall()等
sub(),subn()
>>> s = 'hello fishdm' #如果要把fishdm替换成python,那么可以用以前学到的字符串方法replace >>> s.replace('fishdm','python') 'hello python' #但这种方法并不是正则表达式,只是根据指定的字符串进行替换 #这样写,想让它成为一个正则表达式把fishdm改成r'f..m' >>> s.replace(r'f..m','python') 'hello fishdm' #明显不起作用,replace()方法并不支持正则表达式 #所以我们有必要在正则表达式当中准备能够实现这样操作的函数,sub >>> s = 'hello fishdm, fishkm,fisham,foskm,feuio,foimn' >>> rs = r'f....m' #注意,这里的.是字符串中fishdm的f和m之间的字母数 >>> re.sub(rs,'python',s) 'hello python, python,python,foskm,feuio,foimn' >>> re.subn(rs,'python',s) #subn()是返回替换次数,一共替换了多少次 ('hello python, python,python,foskm,feuio,foimn', 3) >>>
spilt() 分割
>>> ip = '10.0.0.252' >>> ip.split('.') #之前学过的字符串分割方法 ['10', '0', '0', '252'] #同样不支持正则表达式 #比如这样一个字符串 >>> s = '123 + 456 -789 * 025' #我想用+-*分割字符串,用split()方法是不行的,该方法只支持一个字符串分割 >>> re.split(r'+-*',s) #注意,这样也是不行的,因为在元字符部分学习过,+-*这三个符号是元字符 #需要添加转义符\,不然会报错 如 >>> re.split(r'[\+\-\*]',s) ['123 ', ' 456 ', '789 ', ' 025'] >>>
正则表达式还有很多内置的函数,可以通过idle查看
>>> dir(re)
['A', 'ASCII', 'DEBUG', 'DOTALL', 'I', 'IGNORECASE', 'L', 'LOCALE', 'M', 'MULTILINE', 'S', 'Scanner', 'T', 'TEMPLATE', 'U', 'UNICODE', 'VERBOSE', 'X', '_MAXCACHE', '__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__initializing__', '__loader__', '__name__', '__package__', '__version__', '_alphanum_bytes', '_alphanum_str', '_cache', '_cache_repl', '_compile', '_compile_repl', '_expand', '_pattern_type', '_pickle', '_subx', 'compile', 'copyreg', 'error', 'escape', 'findall', 'finditer', 'functools', 'match', 'purge', 'search', 'split', 'sre_compile', 'sre_parse', 'sub', 'subn', 'sys', 'template']
>>>
>>> help(re)
可以查看RE手册(考验英文水平的时候到了)
还有其他属性请参考:http://m.blog.csdn.net/blog/daillo/7030879 关于松散正表达式的描述
例如:
# 必须引入 re 标准库
import re
# 字符串替换: sub() 与 subn()
s = '100 NORTH MAIN ROAD'
# 将字符串结尾的单词“ROAD”替换成“RD.”;该 re.sub() 函数执行基于正则表达式的字符串替换。
print(re.sub(r'\bROAD$', 'RD.', s)) # 打印: 100 NORTH MAIN RD.
## subn() 与 sub() 作用一样,但返回的是包含新字符串和替换执行次数的两元组。
print(re.subn(r'\bROAD$', 'RD.', s)) # 打印: ('100 NORTH MAIN RD.', 1)
# 字符串分割, split()
# 在正则表达式匹配的地方将字符串分片,将返回列表。只支持空白符和固定字符串。可指定最大分割次数,不指定将全部分割。
print(re.split(r'\s+', 'this is a test')) # 打印: ['this', 'is', 'a', 'test']
print(re.split(r'\W+', 'This is a test.', 2)) # 指定分割次数,打印:['this', 'is', 'a test']
# 如果你不仅对定界符之间的文本感兴趣,也需要知道定界符是什么。在 RE 中使用捕获括号,就会同时传回他们的值。
print(re.split(r'(\W+)', 'This is a test.', 2)) # 捕获定界符,打印:['this', ' ', 'is', ' ', 'a test']
## `MatchObject` 实例的几个方法
r = re.search(r'\bR(OA)(D)\b', s)
print(r.groups()) # 返回一个包含字符串的元组,可用下标取元组的内容,打印: ('OA', 'D')
print(r.group()) # 返回正则表达式匹配的字符串,打印: ROAD
print(r.group(2)) # 返回捕获组对应的内容(用数字指明第几个捕获组),打印: D
print(r.start()) # 返回匹配字符串开始的索引, 打印: 15
print(r.end()) # 返回匹配字符串结束的索引,打印: 19
print(r.span()) # 返回一个元组包含匹配字符串 (开始,结束) 的索引,打印: (15, 19)
# 匹配多个内容, findall() 返回一个匹配字符串行表
p = re.compile('\d+')
s0 = '12 drummers drumming, 11 pipers piping, 10 lords a-leaping'
print(p.findall(s0)) # 打印: [12, 11, 10]
print(re.findall(r'\d+', s0)) # 也可这样写,打印: [12, 11, 10]
# 匹配多个内容, finditer() 以迭代器返回
iterator = p.finditer(s0)
# iterator = re.finditer(r'\d+', s0) # 上句也可以这样写
for match in iterator:
print(match.group()) # 三次分别打印:12、 11、 10
# 记忆组
print(re.sub('([^aeiou])y$', 'ies', 'vacancy')) # 将匹配的最后两个字母替换掉,打印: vacanies
print(re.sub('([^aeiou])y$', r'\1ies', 'vacancy')) # 将匹配的最后一个字母替换掉,记忆住前一个(小括号那部分),打印: vacancies
print(re.search('([^aeiou])y$', 'vacancy').group(1)) # 使用 group() 函数获取对应的记忆组内容,打印: c
# 记忆组(匹配重复字符串)
p = re.compile(r'(?P<word>\b\w+)\s+\1') # 注意, re.match() 函数不能这样用,会返回 None
p = p.search('Paris in the the spring')
# p = re.search(r'(?P<word>\b\w+)\s+\1', 'Paris in the the spring') # 这一句可以替换上面两句
print(p.group()) # 返回正则表达式匹配的所有内容,打印: the the
print(p.groups()) # 返回一个包含字符串的元组,打印: ('the',)
# 捕获组
r = re.search(r'\bR(OA)(D)\b', s) # 如过能匹配到,返回一个 SRE_Match 类(正则表达式匹配对象);匹配不到则返回“None”
# `MatchObject` 实例的几个方法
if r: # 如果匹配不到,则 r 为 None,直接执行下面语句则会报错;这里先判断一下,避免这错误
print(r.groups()) # 返回一个包含字符串的元组,可用下标取元组的内容,打印: ('OA', 'D')
print(r.group()) # 返回正则表达式匹配的字符串,打印: ROAD
print(r.group(2)) # 返回捕获组对应的内容(用数字指明第几个捕获组),打印: D
# 无捕获组
print(re.match("([abc])+", "abcdefab").groups()) # 正常捕获的结果: ('c',)
print(re.match("(?:[abc])+", "abcdefab").groups()) # 无捕获组的结果: ()
# 命名组
m = re.match(r'(?P<word>\b\w+\b) *(?P<word2>\b\w+\b)', 'Lots of punctuation')
print(m.groups()) # 返回正则表达式匹配的所有内容,打印:('Lots', 'of')
print(m.group(1)) # 通过数字得到对应组的信息,打印: Lots
print(m.group('word2')) # 通过名称得到对应组的信息,打印: of
# 命名组 逆向引用
p = re.compile(r'(?P<word>\b\w+)\s+(?P=word)') # 与记忆组一样用法, re.match() 函数同样不能这样用,会返回 None
p = p.search('Paris in the the spring') # r'(?P<word>\b\w+)\s+(?P=word)' 与 r'(?P<word>\b\w+)\s+\1' 效果一样
print(p.group()) # 返回正则表达式匹配的所有内容,打印: the the
print(p.groups()) # 返回一个包含字符串的元组,打印: ('the',)
# 使用松散正则表达式,以判断罗马数字为例
pattern = '''
^ # beginning of string
(M{0,3}) # thousands - 0 to 3 Ms
(CM|CD|D?C{0,3}) # hundreds - 900 (CM), 400 (CD), 0-300 (0 to 3 Cs),
# or 500-800 (D, followed by 0 to 3 Cs)
(XC|XL|L?X{0,3}) # tens - 90 (XC), 40 (XL), 0-30 (0 to 3 Xs),
# or 50-80 (L, followed by 0 to 3 Xs)
(IX|IV|V?I{0,3}) # ones - 9 (IX), 4 (IV), 0-3 (0 to 3 Is),
# or 5-8 (V, followed by 0 to 3 Is)
$ # end of string
'''
print(re.search(pattern, 'M')) # 这个没有申明为松散正则表达式,按普通的来处理了,打印: None
print(re.search(pattern, 'M', re.VERBOSE).groups()) # 打印: ('M', '', '', '')
# (?iLmsux) 用法
# 以下这三句的写法都是一样的效果,表示忽略大小写,打印: ['aa', 'AA']
print(re.findall(r'(?i)(aa)', 'aa kkAAK s'))
print(re.findall(r'(aa)', 'aa kkAAK s', re.I))
print(re.findall(r'(aa)', 'aa kkAAK s', re.IGNORECASE))
# 可以多种模式同时生效
print(re.findall(r'(?im)(aa)', 'aa kkAAK s')) # 直接在正则表达式里面写
print(re.findall(r'(aa)', 'aa kkAAK s', re.I | re.M)) # 在参数里面写
print(re.findall(r'(aa)', 'aa kkAAK s', re.I or re.M))
# 预编译正则表达式解析的写法
# romPattern = re.compile(pattern) # 如果不是松散正则表达式,则这样写,即少写 re.VERBOSE 参数
romPattern = re.compile(pattern, re.VERBOSE)
print(romPattern.search('MCMLXXXIX').groups()) # 打印: ('M', 'CM', 'LXXX', 'IX')
print(romPattern.search('MMMDCCCLXXXVIII').groups()) # 打印: ('MMM', 'DCCC', 'LXXX', 'VIII')
# match()、search()、sub()、findall() 等等都可以这样用
match() vs search()
match() 函数只检查 RE 是否在字符串开始处匹配,而 search() 则是扫描整个字符串。记住这一区别是重要的。
match() 只报告一次成功的匹配,它将从 0 处开始;如果匹配不是从 0 开始的, match() 将不会报告它。
search() 将扫描整个字符串,并报告它找到的第一个匹配。
例:
print(re.match('super', 'superstition').span()) # 打印: (0, 5)
print(re.match('super', 'insuperable')) # 打印: None
print(re.search('super', 'superstition').span()) # 打印: (0, 5)
print(re.search('super', 'insuperable').span()) # 打印: (2, 7)