拓展:正则表达式-常用函数

该部分为中谷教育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)

posted @ 2014-03-04 00:45  小丑戌  阅读(900)  评论(0编辑  收藏  举报