python正则表达式

判断一个字符串是否是合法的Email地址,虽然可以编程提取@前后的子串,再分别判断是否是单词和域名,但这样做不但麻烦,而且代码难以复用。

1、简单的匹配规则:

a.【\d】【\w】用\d可以匹配一个数字,\w可以匹配一个字母或数字
    例如:
    '00\d'可以匹配'007',但无法匹配'00A';
    '\d\d\d'可以匹配'010';
    '\w\w\d'可以匹配'py3';

b.【.】可以匹配任意字符
    'py.'可以匹配'pyc'、'pyo'、'py!'等等

c.匹配变长的字符
    【*】用*表示任意个字符(包括0个)
    【+】用+表示至少一个字符
    【?】用?表示0个或1个字符
    【{n}】用{n}表示n个字符
    【{n,m}】用{n,m}表示n-m个字符。
    例如:\d{3}\s+\d{3,8}
        \d{3}表示匹配3个数字,例如'010';
        \s可以匹配一个空格(也包括Tab等空白符),所以\s+表示至少有一个空格,例如匹配' ',' '等;
        \d{3,8}表示3-8个数字,例如'1234567'。
        综合起来,上面的正则表达式可以匹配以任意个空格隔开的带区号的电话号码。

        注意:如果要匹配'010-12345'这样的号码呢?由于'-'是特殊字符,在正则表达式中,要用'\'转义,所以,上面的正则是\d{3}\-\d{3,8}。
            但是,仍然无法匹配'010 - 12345',因为带有空格。所以我们需要更复杂的匹配方式。

d.匹配范围【[]】
    [0-9a-zA-Z\_]可以匹配一个数字、字母或者下划线;
    [0-9a-zA-Z\_]+可以匹配至少由一个数字、字母或者下划线组成的字符串,比如'a100','0_Z','Py3000'等等;
    [a-zA-Z\_][0-9a-zA-Z\_]*可以匹配由字母或下划线开头,后接任意个由一个数字、字母或者下划线组成的字符串,也就是Python合法的变量;
    [a-zA-Z\_][0-9a-zA-Z\_]{0, 19}更精确地限制了变量的长度是1-20个字符(前面1个字符+后面最多19个字符)。

e.选择匹配【|】
    A|B可以匹配A或B,所以(P|p)ython可以匹配'Python'或者'python'。

f.指定开头【^】
    ^表示行的开头
    例如:^\d表示必须以数字开头。

    特别注意:[^]这个是取非的含义
    例如:
        key = r"mat cat hat pat"
        p1 = r"[^p]at"#这代表除了p以外都匹配
        pattern1 = re.compile(p1)
        print pattern1.findall(key) # 结果 ['mat', 'cat', 'hat']


g.指定结束【$】
    $表示行的结束
    例如:\d$表示必须以数字结束。

2、re模块(regular expression):

Python提供re模块,包含所有正则表达式的功能。
特别注意:由于Python的字符串本身也用\转义,在字符串中如果有\则会出错。
    例如:
        s = "abc\\def"
        print s #打印的结果是abc\def,这样字符串就变了

        解决方法,在字符串前面加r
        s = r"abc\\def"
        print s #打印的结果就是正常的abc\\def
a.match方法匹配结果
        #_*_coding:utf-8_*_
        import re

        if __name__ == "__main__":
            s = r"010-12345"
            pattern_1 = r"^\d{3}\-\d{3,8}$" # 匹配3个数字开头,中间-分隔,3-8个数字结束
            pattern_2 = r"^\d{2}\-\d{3,8}$" # 匹配2个数字开头,中间-分隔,3-8个数字结束
            result_1 = re.match(pattern_1,s)
            result_2 = re.match(pattern_2,s)

            print result_1 # 匹配上了,打印结果为 <_sre.SRE_Match object at 0x020069F8> 是一个match对象
            print result_2 # 没有匹配上,打印的结果 None
b.使用re分隔字符串:
    我们可以直接用split方法,通过指定的符号分隔字符串。但是有个问题。
    例如:
            s = 'a b   c'
            print s.split(' ') # 打印的结果:['a', 'b', '', '', 'c'],这个并不是我们想要的结果,我们不想list中有空格

        可以用re的split方法
            s = 'a b   c'
            pattern_1 = r"\s+"
            print re.split(pattern_1,s) # 打印的结果:['a', 'b', 'c'],结果是我们想要的

        如果字符串里面有其它字符也要分隔
            s =  'a,b, c    d'
            pattern_2 = r"[\s,]+" # 只要修改这个pattern就可以
            print re.split(pattern_2, s)  # 打印的结果:['a', 'b', 'c', 'd']
    备注:正则表达式来把不规范的输入转化成正确的数组。

c.分组【()】
    我们在a里面演示了,如果匹配上了就返回一个Match对象。
    除了简单地判断是否匹配之外,正则表达式还有提取子串的强大功能。用()表示的就是要提取的分组
    例如:
            s = r"010-12345"
            pattern_group = r"^(\d{3})-(\d{3,8})$"
            m = re.match(pattern_group,s)
            print m.groups() # 打印结果:('010', '12345')
            print m.group(0) # 打印结果:010-12345 原始字符串
            print m.group(1) # 打印结果:010 第一个group
            print m.group(2) # 打印结果:12345 第二个group
        注意:这个和apattern不同的地方在于,我们加了()分组,便于后续提取

    再看一个例子,识别合法的时间
            t = '19:05:30'
            m = re.match(r'^(0[0-9]|1[0-9]|2[0-3]|[0-9])\:(0[0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9]|[0-9])\:(0[0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9]|[0-9])$', t)
            print m.groups() # 打印结果 ('19', '05', '30')
    再看一个分组的例子
            a = 'hello alex alex adn acd'
            print re.findall('(a\w+)',a) # ['alex', 'alex', 'adn', 'acd']
            print re.findall('(a)(\w+)',a) # [('a', 'lex'), ('a', 'lex'), ('a', 'dn'), ('a', 'cd')];分了2组,每个list元素是一个元组
d.贪婪匹配
    需要特别指出的是,正则匹配默认是贪婪匹配,也就是匹配尽可能多的字符。
    例如:匹配出数字后面的0:
            s = "10023000"
            pattern = r"^(\d+)(0*)$" # 分组匹配,以数字开头,以0结束
            print re.match(pattern,s).groups() # 打印的结果:('10023000', '')
            # 由于\d+采用贪婪匹配,直接把后面的0全部匹配了,结果0*只能匹配空字符串了。
            #并不是我们需要的,我们想把数字后面的多个0匹配出来,这就需要使用非贪婪匹配了

            pattern_1 =  r"^(\d+?)(0*)$" # 加个 ? ,使用非贪婪匹配
            print re.match(pattern_1,s).groups() # 打印的结果:('10023', '000'),这样就匹配出来了
e.编译
    当我们在Python中使用正则表达式时,re模块内部会干两件事情:
        编译正则表达式,如果正则表达式的字符串本身不合法,会报错;
        用编译后的正则表达式去匹配字符串。
    如果一个正则表达式要重复使用几千次,出于效率的考虑,我们可以预编译该正则表达式
    例如:
            s = "010-12345"
            pattern = r"^(\d{3})\-(\d{3,8})$"
            comp = re.compile(pattern) # 编译
            print comp #打印结果:<_sre.SRE_Pattern object at 0x026C3420>
            print comp.match(s).groups() # 打印结果:('010', '12345')

            # 直接用以编译的pattern来匹配字符串
            p = comp.match("1231-1115")
            if p!= None:
                print p.groups()
            else:
                print "未匹配上"

3、re模块的常用函数介绍:

a.match
    上面我们已经使用过了match这个函数。
    使用指定正则去待操作字符串中寻找可以匹配的子串, 返回匹配上的第一个字串,并且不再继续找。
    例如:
            s = '''first line
            second line
            third line'''

            regex = re.compile("\w+")
            m = regex.match(s)
            print m # <_sre.SRE_Match object at 0x0000000002BCA8B8>
            print m.group() # first

            # s 的开头是 "f", 但正则中限制了开始为 i 所以找不到
            regex = re.compile("^i\w+")
            print regex.match(s) # None
b.findall
    函数作用为在待操作字符串中寻找所有匹配正则表达式的字串,返回一个列表,如果没有匹配到任何子串,返回一个空列表。
    例如:
            s = '''first line
            second line
            third line'''
            regex = re.compile("\w+")
            print regex.findall(s) # 返回的是一个list ['first', 'line', 'second', 'line', 'third', 'line']

            # 不使用 compile 直接使用 findall
            print re.findall("\w+", s) # 返回的是一个list ['first', 'line', 'second', 'line', 'third', 'line']
c.search
    search是从字符串做任意匹配
    例如:
            ret_match= re.match("c","abcde")
            print ret_match # None,开头没有匹配到就结束了

            ret_search = re.search("c", "abcde")
            print ret_search.group() # c
        注意:match开头没有匹配到就结束了
match和findall最简单的区别就是:match从开头匹配,匹配到了就结束;findall会匹配所有能匹配的字符串。

4、一些不太常用的表达式语法:

【?= … 】:表达式’…’之前的字符串,特殊构建不作为分组
    例如:
    在字符串’ pythonretest ’中 (?=test) 会匹配’ pythonre ’

【?<= … 】:跟在表达式’…’之后的字符串符合括号之后的正则表达式,特殊构建不作为分组
    例如:
    正则表达式’ (?<=abc)def ’会在’ abcdef ’中匹配’ def 

【\1】:匹配重复的元素
    例如:
            s='1113446777'
            m = re.findall(r'(\d)\1',s) # \1表示重复的元素
            print m # ['1', '4', '7']

            如果把不重复的也提取出来可以:
            s='1113446777'
            m = re.findall(r'(\d)\1*',s) # \1表示重复的元素,在后面加个*,就可以把不重复也匹配出来了
            print m # ['1', '3', '4', '6', '7']

5、实际应用的例子:
a.验证邮箱的正确性

        text = raw_input("Please input your Email address:\n")
        # 以字母数字下划线开头,限定@前字符个数0-19个
        # @后面的数字限定1-13个
        # 以[com,cn,net]其中的一个结束,{2,3}表示.后面的结束字符个数在2-3之间;例如输入test@qq.c就非法,因为结束只有1个字符
        r = re.match(r'^[0-9a-zA-Z_]{0,19}@[0-9a-zA-Z]{1,13}\.[com,cn,net,c]{2,3}$',text)
        if r:
            print('right!')
        else:
            print('error!')

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值