一 python正则简介

  就其本质而言,正则表达式(或 RE)是一种小型的、高度专业化的编程语言,(在Python中)它内嵌在Python中,并通过 re 模块实现。

  正则表达式模式被编译成一系列的字节码,然后由用 C 编写的匹配引擎执行。

  正则表达式并不是Python的一部分。正则表达式是用于处理字符串的强大工具,拥有自己独特的语法以及一个独立的处理引擎,效率上可能不如str自带的方法,但功能十分强大。得益于这一点,在提供了正则表达式的语言里,正则表达式的语法都是一样的,区别只在于不同的编程语言实现支持的语法数量不同;但不用担心,不被支持的语法通常是不常用的部分。如果已经在其他语言里使用过正则表达式,只需要简单看一看就可以上手了。

下图展示了使用正则表达式进行匹配的流程: 

 

正则表达式的大致匹配过程是:依次拿出表达式和文本中的字符比较,如果每一个字符都能匹配,则匹配成功;一旦有匹配不成功的字符则匹配失败。如果表达式中有量词或边界,这个过程会稍微有一些不同,但也是很好理解的,看下图中的示例以及自己多使用几次就能明白。

二  python正则的字符

1、正则表达式导入:

python中re模块提供了正则表达式相关操作,故我们只需要导入re模块就行

导入方法:
        import re 

2、普通字符:

  大多数字符和字母都会和自身匹配

import re  #导入re模块
r=re.findall('alex','yuanaleSxalexwupeiqi')
print(r) #执行结果:['alex']

 3、原生字符r:

首先,我们来看普通字符串,有如下例子:

 >>>foo = "hello"
 >>>bar = r"hello"

 

前者是常规字符串,后者 r 开头的是原始字符串,两者有什么区别?因为在上面的例子中,它们都是由普通文本字符组成的串,在这里没什么区别,下面可以证明:

>>> foo is bar
True
>>> foo == bar
True

其次,如果字符串中包括有特殊字符,会是什么情况呢?再来看一个例子:

>>> foo = "\n"
>>> bar = r"\n"

>>> foo, len(foo)
('\n', 1)
>>> bar, len(bar)
('\\n', 2)
>>> foo == bar
False
>>>

"\n" 是一个转义字符,它在 ASCII 中表示换行符。而 r"\n" 是一个原始字符串,即不对特殊字符进行转义,它就是你看到的字面意思,由 "\" 和 "n" 两个字符组成的字符串。

定义原始字符串可以用小写r或者大写R开头,比如 r"\b" 或者 R"\b" 都是允许的。在 Python 中,正则表达式一般用原始字符串的形式来定义,为什么呢?

举例来说,对于字符 "\b" 来说,它在 ASCII 中是有特殊意义的,表示退格键。而在正则表达式中,它是一个特殊的元字符,用于匹配一个单词的边界,为了能让正则编译器正确地表达它的意义就需要用原始字符串,当然也可以使用反斜杠 "\" 对常规定义的字符串进行转义

>>> foo = "\\b"
>>> bar = r"\b"
>>> foo == bar
True

总结一点:在正则表达式中,总是加上r是不会错的选择

4、基本元字符:

字符:用于代替一个字符
   .  匹配除换行符以外的任意字符
    \  转义字符,使特殊字符具有本来的意义
        如: 1\.2 可以匹配 1.2
   ^ 匹配字符串的开始
   $ 匹配字符串的结束
    [] 指定范围内的的任意单个字符
        如:[0-9]: 0到9的数字
            [q_u]:匹配q/_/u三个元素中任意一个
 [^..] 字符集取反,表示只要不是括号中出现的字符都可以匹配,注意:在[]里只有^/-/[]以及\有特殊含义。
        如:a[^bcd]e 可匹配 aee、afe等
>>> re.match(r"a.c", "abc").group() #用.匹配一个字符
'abc'
>>> re.match(r"a.c", "abcef").group()
'abc'
>>> re.match(r"1\.2", "1.2").group() #用转义符\将.变为本来意义
'1.2'
>>> re.match(r"a[0-9]b", "a2b").group() #用[]来匹配一个范围内的字符
'a2b'
>>> re.match(r"a[0-9]b", "a5b11").group()
'a5b'
>>> re.match(r"a[.*?]b", "a.b").group()
'a.b'
>>> re.match(r"abc[^\w]", "abc!123").group()
'abc!

5、转义元字符:

元字符\ :对一些字母转义,并赋予特殊的意义
    \d 匹配任何十进制数;它相当于类 [0-9]。
    \D 匹配任何非数字字符;它相当于类 [^0-9]。
    \s 匹配任何空白字符;它相当于类 [ \t\n\r\f\v]。
    \S 匹配任何非空白字符;它相当于类 [^ \t\n\r\f\v]。
    \w 匹配任何字母数字字符;它相当于类 [a-zA-Z0-9_]。
    \W 匹配任何非字母数字字符;它相当于类 [^a-zA-Z0-9_]
    \b 匹配一个单词边界,也就是指单词和空格间的位置。(详见“位置锚定”)
>>> a = "123 ..*#$% ABC abc"<br>>>> re.findall("\d",a)
['1', '2', '3']
>>> re.findall(r"\s",a)
[' ', ' ', ' ']
>>> re.findall(r"\w",a)
['1', '2', '3', 'A', 'B', 'C', 'a', 'b', 'c']
>>> re.findall(r"\D",a)
[' ', '.', '.', '*', '#', '$', '%', ' ', 'A', 'B', 'C', ' ', 'a', 'b', 'c']
>>> re.findall(r"\S",a)
['1', '2', '3', '.', '.', '*', '#', '$', '%', 'A', 'B', 'C', 'a', 'b', 'c']
>>> re.findall(r"\W",a)
[' ', '.', '.', '*', '#', '$', '%', ' ', ' ']
>>> re.findall(r"\babc\b","abc abcdeafefeabc")
["abc"

6、边界匹配:

位置锚定: 用于指定字符出现的位置
    ^ 用于锚定行首
         如:^Char   以Char开头
    $ 锚定行尾,注意只是行尾
         如:char$  以char结尾
   ^$ 空白行
   \b 锚定词首
         如:\bchar   char单词的c字母是与空格连在一起的
      锚定词尾
         如:char\b   char单词的r字母是与空格连在一起的
         如: \bhello\b 匹配hello单词
>>> re.match(r"^abc","abc").group()
'abc'
>>> re.match(r"abc$","sdsdabc").group() 
'abc'

7、重复匹配:

次数:用于描述一个元素出现的次数
  * 重复0次或更多次
  + 重复1次或更多次
  ? 重复0次或1次
 {n} 重复n次
{n, } 重复n次或更多次
{n, m}重复n到m次
# 简单匹配身份证号码,前面17位是数字,最后一位可以是数字或者字母X
>>> re.match(r"\d{17}[\dX]", "42350119900101153X").group()
'42350119900101153X'

# 匹配5到12的QQ号码
>>> re.match(r"\d{5,12}$", "4235011990").group()
'4235011990'

>>>re.findall(r"abc*","sds dab abcdfeabccc")
['ab', 'abc', 'abccc']
>>>re.findall(r"abc+","sds dab abcdfeabccc")
['abc', 'abccc']
>>>re.findall(r"abc?","sds dab abcdfeabccc")
['ab', 'abc', 'abc']
>>>

8、逻辑匹配:

逻辑分支条件字符 |,它把表达式分为左右两部分,先尝试匹配左边部分,如果匹配成功就不再匹配后面部分了,这是逻辑 "或" 的关系

# abc|cde 可以匹配abc 或者 cde,但优先匹配abc
>>> re.match(r"aa(abc|cde)","aaabccde").group()
'aaabc'

#0\d{2}-\d{8}|0\d{3}-\d{7} 表达式以0开头,既可以匹配3位区号8位号码,也可以匹配4位区号7位号码
>>> re.match(r"0\d{2}-\d{8}|0\d{3}-\d{7}", "0755-4348767").group()
'0755-4348767'
>>> re.match(r"0\d{2}-\d{8}|0\d{3}-\d{7}", "010-34827637").group()
'010-34827637'

9、分组:

前面介绍的匹配规则都是针对单个字符而言的,如果想要重复匹配多个字符怎么办,答案是,用子表达式(也叫分组)来表示,分组用小括号"()"表示,例如 (abc){2} 表示匹配abc两次。

关于分组,group 方法可用于提取匹配的字符串分组,默认它会把整个表达式的匹配结果当做第0个分组,就是不带参数的 group() 或者是 group(0)。但是,group(1)获取第一组括号中的内容,以此类推

>>> m = re.match(r"(\d+)(\w+)", "123abc")
#分组0,匹配整个正则表达式
>>> m.group()
'123abc'
#等价
>>> m.group(0)
'123abc'
# 分组1,匹配第一对括号
>>> m.group(1)
'123'
# 分组2,匹配第二对括号
>>> m.group(2)
'abc'

 #注意区别:

import re
origin = "hello ale1x bcd abcd lge ale2x acd 19"
#此时,*匹配\w。代表可以有0到n个\w元素
r=re.findall(r"a\w*",origin)
print(r) #执行结果:['alex', 'abcd', 'acd']

#此时,*同样匹配的是\w,不同的是findall只是输出分组中的内容
r=re.findall(r"a(\w*)",origin)print(r) #执行结果:['lex', 'bcd', 'cd']

#此时,*匹配的是\w这个分组,即等同于可以有0到n个(\w)如:(\w)(\w)(\w)...。此时python的re模块中有一个规则就是,当书写成"(\dalex)*"。只会匹配输出最后一次出现的
r=re.findall(r"(\dalex){2}","hello 2alex3alex lge 4alex 19")  #目的匹配的是:“2alex3alex”,但是在取值的时候,只会提取最后一次出现的
print(r) #执行结果:['3alex']  

 三、正则函数的应用:

1、match:

  re.match() #只匹配开头,如果开头没有匹配到对象就返回NONE

 

#未分组
import re
origin = "hello alex bcd abcd lge acd 19"
r=re.match("h\w*",origin)
print(r) #执行结果:<_sre.SRE_Match object; span=(0, 5), match='hello'>
# group() 就是从match对象中取出元素
print(r.group())  #执行结果:hello
#因groups()和groupdict()都是提取分组元素的,故为空
print(r.groups()) # 执行结果:()
print(r.groupdict())#执行结果:{}

#分组
import re
origin = "hello alex bcd abcd lge acd 19"
#(\w)将\w括起来,就是代表对\w进行分组,分组的意义是:在匹配成功的范围内,再进行匹配。
r=re.match("h(\w*)",origin)
print(r) #执行结果:<_sre.SRE_Match object; span=(0, 5), match='hello'>
print(r.group()) #执行结果:hello
print(r.groups())#执行结果:('ello',)
#注意?P<name> 的意思是:对分组的内容定一个key值,
r=re.match("h(?P<name>\w*)",origin)
print(r.groupdict()) #执行结果:{'name': 'ello'}

 

2、search:

  浏览整个字符串去匹配第一个,未匹配成功返回None

#未分组
import re
origin = "hello alex bcd abcd lge acd 19"
r=re.search("a\w*",origin)
print(r) #执行结果:<_sre.SRE_Match object; span=(6, 10), match='alex'>
print(r.group()) #执行结果:alex
print(r.groups()) #执行结果:()
print(r.groupdict()) #执行结果:{}

#分组
import re
origin = "hello alex bcd abcd lge acd 19"
r=re.search("a(\w*)",origin)
print(r) #执行结果:<_sre.SRE_Match object; span=(6, 10), match='alex'>
print(r.group()) #执行结果:alex
print(r.groups()) #执行结果:('lex',)
r=re.search("h(?P<name>\w*)",origin)
print(r.groupdict()) #执行结果:{'name': 'ello'}

3、findall:

  将匹配到的所有内容都放置在一个列表中

#未分组
import re
origin = "hello alex bcd abcd lge acd 19"
r=re.findall("a\w*",origin)
print(r) #执行结果:['alex', 'abcd', 'acd']
#print(r.group()) #没有这种用法

#分组
import re
origin = "hello alex bcd abcd lge acd 19"
r=re.findall("a(\w*)",origin)
print(r) #执行结果:['lex', 'bcd', 'cd']
#print(r.groups()) #没有这种用法

4、finditer:

  将匹配到的所有内容都放置在迭代器中

#未分组
import re
origin = "hello alex bcd abcd lge acd 19"
#注意:finditer 以iter结尾的单词,iter在大多数情况下表示的可迭代的意思
r=re.finditer("a\w*",origin)
print(r) #执行结果:<callable_iterator object at 0x0038F370>
for i in r:
    print(i.group())
"""
执行结果:
<callable_iterator object at 0x006FF890>
alex
abcd
acd
"""

#分组
import re
origin = "hello alex bcd abcd lge acd 19"
#注意:finditer 以iter结尾的单词,iter在大多数情况下表示的可迭代的意思
r=re.finditer("a(\w*)",origin)
print(r) #执行结果:<callable_iterator object at 0x0051F370>
for i in r:
    print(i.group(),i.groups())
r=re.finditer("a(?P<name>\w*)",origin)
for i in r:
    print(i.groupdict())

"""
执行结果:
<callable_iterator object at 0x00708A50>
alex ('lex',)
abcd ('bcd',)
acd ('cd',)
{'name': 'lex'}
{'name': 'bcd'}
{'name': 'cd'}
"""

 5、split:

  分割,注意分割时加有分组和未分组的区别

import re
origin = "hello alex bcd abcd lge acd 19"
#未分组 #未分组的是不会将匹配的内容输出的
r=re.split("a\w+",origin,1) #注意这个flags=1,表示的是分割的次数。不填写默认为全部分割 print(r) #执行结果:['hello ', ' bcd abcd lge acd 19'] #分组 #分组了,会将匹配的内容一同输出
r=re.split("a(\w+)",origin) print(r) #执行结果:['hello ', 'lex', ' bcd ', 'bcd', ' lge ', 'cd', ' 19']

练习题:计算器

#计算器
import re
origin="1 - 2 * ((60-30+1*(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))"
while True:
    r=re.split("\(([^()]+)\)",origin,1) #注意这个flags=1,表示的是分割的次数。不填写默认为全部分割
    if len(r)==3:
        r[1]=str(eval(r[1]))
        origin="".join(r)
        print(origin)
    else:
        print(eval(origin)) #-347153.161904762
        break

6、sub和subn:

  都是用于替换,sub和subn的区别就在于,sub替换后返回字符串,subn替换后,返回的是元组,元组元素包括:替换后的字符串,和替换次数

import re
origin = "hello alex bcd abcd lge acd 19"
r=re.sub("a\w+","kkk",origin,1) #注意这个flags=1,表示的是替换的次数。不填写默认为全部替换
print(r) #执行结果:hello kkk bcd abcd lge acd 19
r=re.subn("a\w+","kkk",origin)  #注意这个flags=1,表示的是替换的次数。不填写默认为全部替换
print(r) #执行结果:('hello kkk bcd kkk lge kkk 19', 3)

 

posted on 2017-05-25 17:02  进_进  阅读(162)  评论(0)    收藏  举报