关于 python中的正则表达的基础用法

关于正则表达式

在尚未接触python的时候,就已听说python在文本处理方面,其便利程度是其他语言无法相提并论的,这除了得益于python本身的语法设计十分便利以外,更多的还要归功于python对于正则表达式的集成。对于一些重复性的繁琐的文本处理操作,使用正则表达式来进行处理是不二的选择,本人也是经过了几天的学习,通过翻阅了几个大佬的博客之后,不敢说对于正则表达式有掌握的很好,但也算是有所入门,不过所谓好记性不如烂笔头,只有通过自己的总结,学到的东西才能算是自己的,所以决定记录一下自己在学习python正则表达式的要点,由于经验尚浅,如有错误的地方,还请在评论处慷慨指出。

初识正则表达式

话不多说,首先我们通过一个栗子,来初步认识一下什么是正则表达式,在本文中,会使用 source 表示需要被处理的文本内容,taik is cheap,show u my code…

import re
if __name__ == "__main__":
	source = "123wellhold456wellhold"
	re.findall(r"wellhold",s)

输出结果:

['wellhold', 'wellhold']

简单的解释一下代码,re这个模块,就是python集成了正则表达式的模块,在这个模块当中封装了很多关于正则表达式使用的方法,之后会对这些方法详细的介绍,这里只需要初步了解一下正则表达式,不过看了上面的demo,你可能会说,“这是什么东西?有啥用啊?”,不用着急,通过我们慢慢深入的学习,会明白正则的强大之处的。

正则表达式的基本语法

学习正则,就像是学习一门新的编程语言,首先要先看看它的基本语法,下表中列出了正则表达式的一些表达式代表的含义:

表达式描述
.(英文输入法当中的点)在普通模式下除了\n之外,可以表示任何字符,包括自己
*前一个表达式或者字符匹配0次、1次或者多次
+前一个表达式或者字符匹配至少1次或者多次
前一个表达式或者字符匹配0次或1次,非贪婪模式
[abc]仅匹配 a 或 b 或 c 字符
[^abc]仅不匹配 a 或 b 或 c 字符
^匹配字符串的开头
$匹配字符串的结尾
{n}精确匹配前一个表达式 n 次
{n,}匹配前一个表达式至少 n 次
{n,m}匹配前一个表达式 n 到 m 次
{,m}匹配前一个表达式最多 m 次
a | b匹配 a 或 b
(表达式)小括号包裹的表达式定义为一个分组
(?:表达式)通过?:修饰的表达式,不定义为一个分组
(?# 注释)通过?#修饰的小括号内的内容表示注释
(?<=abc)希望匹配的字符串的前面应该出现abc字符串
(?=abc)希望匹配的字符串的后面应该出现abc字符串
(?<!abc)希望匹配的字符串的前面不应该出现abc字符串
(?!abc)希望匹配的字符串的后面不应该出现abc字符串
\b匹配一个单词边界,也就是指单词和空格间的位置。例如, ‘er\b’ 可以匹配"never" 中的 ‘er’,但不能匹配 “verb” 中的 ‘er’
\B匹配非单词边界。‘er\B’ 能匹配 “verb” 中的 ‘er’,但不能匹配 “never” 中的 ‘er’
\1…\9匹配第n个分组的子表达式

除了上表列出的基础表达式以外,下表还列举了在正则表达式中常用的字符集表达式

表达式描述
\d匹配一个数字字符,等价于[0-9]
\D匹配一个非数字的字符,等价于[^0-9]
\s匹配任何空白字符,包括空格、制表符、换页,等价于 [ \f\n\r\t\v]
\S匹配任何非空白字符,等价于 [^ \f\n\r\t\v]。
\w匹配包括下划线的任何单词字符。等价于 [A-Za-z0-9_]
\W匹配任何非单词字符。等价于 [^A-Za-z0-9_]

第一次看到上面的两张表,可能你会头疼,wtf,这一堆奇奇怪怪的东西是啥?没关系,接下来我们通过举个栗子来让我们更加深切的了解这些表达式的含义和用法

. 表达式用法

该表达式在普通模式中,匹配除了 \n 以外的所有字符,如果是在S模式之下,则也匹配 \n,举个栗子:

import re
if __name__ == "__main__":
    source = "wellhold + 123 = o_O #"
    print re.findall(r".+", source)

输出结果

['wellhold + 123 = o_O #']

从输出结果看来,所以的字符都被匹配了,这时候我们加上\n

import re
if __name__ == "__main__":
    source = "wellhold + 123 \n = o_O #"
    print re.findall(r".+", source)

输出结果

['wellhold + 123 ', ' = o_O #']

可以看到字符串被分割成了两个元素,说明\n无法识别,这时候,我们使用S模式再试一次

import re
if __name__ == "__main__":
    source = "wellhold + 123 \n = o_O #"
    print re.findall(r".+", source,re.S)

输出结果

['wellhold + 123 \n = o_O #']

我们再使用findall方法(关于方法后边会具体说,这里大家就体会一下就好了)的时候,第三个参数加上了re.S,这时候\n就被识别了,验证了上述内容

+ * ?三种表达式的用法

* 则表示表示前一个字符或者表达式匹配 0次 1次 或多次,我们将上面的栗子修改一下

import re
if __name__ == "__main__":
    source = "wellold"
    print re.findall(r"wellh*old", source)

输出结果

['wellold']

可以看到在 source 中是没有 h 这个字母的,但是并不影响匹配结果。

而+ 表达式表示前一个字符或者表达式匹配 至少 1次,即 1次 或多次,还是上面的那个栗子

import re
if __name__ == "__main__":
    source = "wellold"
    print re.findall(r"wellh+old", source)

输出结果

[]

可以看到将 * 换成了 + 之后,就匹配不到结果了,因为 + 要求 h 至少要匹配一次

而? 表达式表示前一个字符或者表达式匹配 0次或1次

import re
if __name__ == "__main__":
    source = "wellold"
    print re.findall(r"wellh?old", source)
    source = "wellhold"
    print re.findall(r"wellh?old", source)
    source = "wellhhold"
    print re.findall(r"wellh?old", source)

输出结果

['wellold']
['wellhold']
[]

可以看到,当变成了 hh 之后,表达式就匹配不到了。

+? *? ?? 表达式用法

上边讲完了 + * ? 的用法以后,我们再来说说在这三个符号后边再加上一个 ? 是什么含义呢?说到这个,就必须提到一个概念——贪婪模式,顾名思义,贪婪模式就是很贪,表达式想要匹配到更多,更长的字符串,而 + * ?在默认情况下,就是采用的贪婪模式进行匹配的,多说无益,亮出你的代码!

import re
if __name__ == "__main__":
    source = "/* comment1 */ code /* comment2 */"
    print re.findall(r"/\*.*\*/", source)

简单解释一下这里的正则表达式,因为在正则表达式中,*是特殊符号,表示重复匹配前一个表达式 0 1 或多次,但在这里我们需要匹配 * 号本身,所以我们需要 \ 反斜杠字符进行转义,然后我们来看看输出结果

['/* comment1 */ code /* comment2 */']

结果是把整个字符串匹配进去了,这时候你可能会发现,WTF?为什么有两个 */ 却匹配了最后一个,是不是看不起我们前边的 */ 啊?哈哈,这就是贪婪模式, ‘.*’ 在贪婪模式下想要匹配更长的字符串,于是就了这样的现象。所谓知足者常乐,我们想让它知足,想要把贪婪模式取消,这时候 *? 就登场了。

import re
if __name__ == "__main__":
    source = "/* comment1 */ code /* comment2 */"
    print re.findall(r"/\*.*?\*/", source)

输出结果

['/* comment1 */', '/* comment2 */']

可以看到 第一个 /* 就匹配了第一个 */,而第二个/*也单独匹配了最后一个*/,其他的 +? 和 ?? 同理,这里就不举例了

[ abc] 与 [^abc] 表达式用法

通过表中的描述,可以简单的知道这个表达式的用法,表示的是:要求在待处理文本中必须匹配到任意一个 a 或 b 或 c
除此之外,[ ]还可以通过 ’ - ‘ 减号来指定一个字符集合的范围, [a-zA-Z] 指定的就是英文字母的大小写,注意,在这里是不可以把 a z 的顺序颠倒的,举个栗子:

import re
if __name__ == "__main__":
    source = "wellhold"
    print re.findall(r"well[hb]old", source)
    source = "wellbold"
    print re.findall(r"well[hb]old", source)
    source = "wellcold"
    print re.findall(r"well[hb]old", source)

输出结果

['wellhold']
['wellbold']
[]

而在 [ ]中的内容的开头有 ‘^’ 号,则表示取非,即在括号里的字符都不匹配,如 [^a-zA-Z] 表明不匹配所有英文字母。但是如果 ‘^’ 不在开头,则它就不再是表示取非,而表示其本身,如 [a-z^A-Z] 表明匹配所有的英文字母和字符 ’^’ ,老规矩,举个栗子:

import re
if __name__ == "__main__":
    source = "wellhold"
    print re.findall(r"well[^hb]old", source)
    source = "wellbold"
    print re.findall(r"well[^hb]old", source)
    source = "wellcold"
    print re.findall(r"well[^hb]old", source)

输出结果

[]
[]
['wellcold']

{n} {n,m} {n,} {,m} 表达式用法

可以将{ } 看做是指定了一个范围,n到m的范围,要求它修饰的前一个表达式至少出现n到m次,并且一句省略 n 或 m 表示至少 n次 或至多 m次。
首先我们先看 {n} 的栗子

import re
if __name__ == "__main__":
    source = "wellhold"
    print re.findall(r"wel{1}hold", source)
    print re.findall(r"wel{2}hold", source)
    print re.findall(r"wel{3}hold", source)

输出结果

[]
['wellhold']
[]

输出可以看到,要求 l 字母精确2次匹配才行

{n,m}的栗子

import re
if __name__ == "__main__":
    source = "wellhold"
    print re.findall(r"wel{1,4}hold", source)
    print re.findall(r"wel{2,4}hold", source)
    print re.findall(r"wel{3,4}hold", source)

输出结果

['wellhold']
['wellhold']
[]

从结果可以看到,当区间为{3,4}的时候匹配不上

{n,}的栗子与{n,m}的栗子类似,只需要省略 4

{,m}的栗子

import re
if __name__ == "__main__":
    source = "wellhold"
    print re.findall(r"wel{,1}hold", source)
    print re.findall(r"wel{,2}hold", source)
    print re.findall(r"wel{,3}hold", source)

输出结果

[]
['wellhold']
['wellhold']

当 m=1 ,即至多1次的时候,匹配结果为空

?: 表达式用法

当我们在需要将某一部分的表达式作为一个整体进行某些操作,如指定重复次数的操作的时候,一般会对这一部分的表达式用 ()进行包裹,但是仅仅使用(),这样往往是拿不到我们想要的结果的,而还需要使用?:修饰,即(?:abc),总而言之 ?:指的就是不将()内的表达式作为一个组,举个栗子:

import re
if __name__ == "__main__":
    source = "whwhwh whhwhh wwhwwh"
    print re.findall(r"\b(?:wh)+\b", source)

简单解释一下这个正则表达式的含义,\b指的是匹配一个字符串的边界,即字符串与空格之间的位置,所以结合整个正则表达式的含义就是匹配 wh重复n次的字符串,n至少为1,并且该字符串要求两端都为空格。输出结果为:

['whwhwh']

这时候我们将 ?: 去掉之后,再看看输出结果

import re
if __name__ == "__main__":
   source = "whwhwh whhwhh wwhwwh"
   print re.findall(r"\b(wh)+\b", source)

输出结果

['wh']

是不是觉得很神奇?这是因为去掉了 ?: 之后,()内的表达式被视为了一个组,这时候你又会问,WTF?组是什么鬼?不要着急,后边我们会更加详细的说明组的含义,在这里只需要体会到 ?:的作用即可

?# 表达式用法

这个表达式就相当于python当中 # 号,用来编写注释的,举个栗子

import re
if __name__ == "__main__":
   source = "whwhwh whhwhh wwhwwh"
   print re.findall(r"\b(?:wh)(?#这个是一个注释)+\b", source)

输出结果

['whwhwh']

丝毫不影响表达式匹配

(?<=abc) (?=abc) 表达式用法

(?<=abc)表示的是匹配的字符串前缀必须有abc,称作前向界定;而(?=abc) 则表示匹配的字符串后缀必须有abc,称作后向界定,举个栗子

import re
if __name__ == "__main__":
  source = "/* comment1 */ code /* comment2 */"
  print re.findall(r"(?<=/\*).+?(?=\*/)", source)

这里使用的是 +? 非贪婪模式匹配,输出结果

[' comment1 ', ' comment2 ']

需要注意的是,(?<=abc) 中,abc不能是正则表达式,而是必须指定的字符串,而 (?=abc) 中的abc却可以是表达式或字符串,举个栗子。

import re
if __name__ == "__main__":
    source = "ww111hh , ww33 , 233ll"
    print re.findall(r"(?<=[a-z]+)\d+(?=[a-z]+)", source)

输出结果

Traceback (most recent call last):
  File "I:/pythonWorkspace/studypython/test.py", line 11, in <module>
    print re.findall(r"(?<=[a-z]+)\d+(?=[a-z]+)", source)
  File "D:\Python27\lib\re.py", line 181, in findall
    return _compile(pattern, flags).findall(string)
  File "D:\Python27\lib\re.py", line 251, in _compile
    raise error, v # invalid expression
sre_constants.error: look-behind requires fixed-width pattern

可以看到输出结果是报错,但是我们将前缀匹配表达式去掉,再看看

import re
if __name__ == "__main__":
   source = "ww111hh , ww33 , 233ll"
   print re.findall(r"\d+(?=[a-z]+)", source)

输出结果

['111', '233']

说明前缀表达式是无法使用正则表达式的,只能使用具体的字符串进行匹配

(?<!abc) (?!abc) 表达式用法

看到上面所说的前向界定和后向界定,大家肯定一看就知道这小结说的这两个表达式是什么了,没错,就是非前向/后向界定
(?<!abc)表示的匹配的字符串不希望出现abc(同理abc只可以是字符串);而 (?!abc) 表示的匹配的字符串不希望出现abc(即可以是正则表达式,也可以是字符串)多说无益,举个栗子

import re
if __name__ == "__main__":
    source = "ww111hh , ww33, 233jj"
    print re.findall(r"\d+(?![a-z]+)", source)

输出结果

['11', '33', '23']

组的概念

讲完个基本表达式的一些基础用法,那么接下来我们来一起理解一下在正则表达式当中的一个很重要的概念,那就是组,所谓的组,可以理解成把一部分正则表达式声明为一个变量,将这个部分看做一个整体,并且可以为这个变量命名或者编号,已备之后有需要引用这个变量。

( abc ) 无名组

这是最基本的组,可以对比java当中的匿名类来理解一下,我们来举个栗子,能更加真切的体会无名组的作用

import re
if __name__ == "__main__":
    source = "wellhold123wellhold"
    print re.findall(r"[a-z]+\d+[a-z]+", source)
    print re.findall(r"[a-z]+(\d+)[a-z]+", source)
    print re.findall(r"[a-z]+(?:\d+)[a-z]+", source)

输出结果

['wellhold123wellhold']
['123']
['wellhold123wellhold']

可以看到,在第一个表达式当中,没有添加无名组,将整个字符串匹配进来了,在第二个表达式当中,我们将中间的数据变成了无名组,使用()将中间的表达式包裹之后,输出的结果就仅为中间的数字了。这是因为在正则表达式当中的查找方法findall,仅会返回组内的匹配内容,让我们回想一下,一开始说到的 ?: 作用,如果我们不希望只返回组内的内容,但是又不得不需要使用上(),这时候我们就是使用的 ?: ,所以在第三个表达式的输出就又是完全匹配了字符串。

(?P…) (?P=name) 命名组及引用

在了解了无名组之后,我们来看看,如果某个组需要在后半部分用到,那么我们需要给这个组取个名,以便之后的引用时,应该怎么做呢?举个栗子

import re
if __name__ == "__main__":
    source = "wellhold123wellhold wellhold456well well789well"
    print re.findall(r"(?P<name1>[a-z]+)\d+(?P=name1)", source)

输出结果

['wellhold', 'well']

这里需要强调一下,我们看到表达式是匹配 字符 - 数字 - 字符,但在后缀引用中,是引用的名为name1 的命名组,这里的引用并非是引用表达式,而是引用这个命名组匹配的值,所以wellhold456well这个字符串就并未匹配上,可能比较绕,但是需要重点理解一下

\1 … \9 序号引用组

如果说,你想要引用前边的组,但是呢,又懒得为组命名,那有没有什么办法呢?有!咱们可以使用序号去引用组,且看我的代码

import re
if __name__ == "__main__":
    source = "wellhold123wellhold wellhold456well well789well"
    print re.findall(r"([a-z]+)\d+\1", source)

输出结果

['wellhold', 'well']

是不是满足了很多命名困难症的选手的需求呢?

re模块的基本函数方法

上述通过各种栗子,和大伙一起基本了解了正则表达式的各种表达式的基础用法,但想要发挥出正则表达式的威力,除了掌握表达式的基础语法以外,还需要掌握python当中的re模块中的各种基础函数的使用方法,接下来我们来正式讲解一下关于re模块当中的findall、search、match等函数。

re.findall(pattern,source [ , flag ])

相信大家看到这个函数都不会觉得陌生,在之前的栗子中都是用的这个函数,顾名思义,这个函数就是匹配 source 中所有符合 pattern 正则规则的字符串,而可选参数 flag 指的是通过不同的模式执行这一行代码,其函数返回是一个 list
下表是列举了所有模式

模式描述
I忽略大小写的差别
L字符集本地化,主要为了支持多语言版本的字符集使用,如 \w ,英文环境表示[a-zA-Z0-9] ,在法语环境下使用,缺省设置下,不能匹配 “é” 或 “ç” , 使用 L 选项即可匹配。
M多行匹配,一般情况常与 ^ $ 一起使用,请配合本小结的 栗子1 食用理解更佳
S‘.’ 号将匹配所有的字符,包括 \n
U字符集都将使用Unicode编码
X注释模式,使用 ’#’ 来引导一个注释。这样可以让你把规则写得更美观些,请配合本小结的 栗子2 食用理解更佳
# 栗子1
import re
if __name__ == "__main__":
    source = "wellhold \nwellon \nwellience"
    print re.findall(r"^\w+", source)
    print re.findall(r"^\w+", source,re.M)

输出结果

['wellhold']
['wellhold', 'wellon', 'wellience']

从输出结果可以看到,未采用re.M模式运行的仅能匹配到首行,而使用该模式运行的则可以匹配到每一行。

# 栗子2

import re
if __name__ == "__main__":
    source = "wellhold123"
    print re.findall(r""" 
                         # start a rule
                        \d+ #number
                        |[a-zA-Z]+ #word
                      """, source,re.X)

输出结果

['wellhold', '123']

通过使用X模式,就可以像编写一般的python程序一样,编写注释,这样可以使得自己的正则表达更容易被读懂。也便于提高代码可读性

re.match(pattern,source [ , flag ])

这个函数,是咱们从未见过的 “ 船新版本” 和 search函数 二者可说是正则表达式中核心的函数了,其功能是匹配符合pattern的字符串不过其返回若是成功匹配,则是一个Match对象,否则则是返回None,所以常规操作都会进行返回值进行判断,需要注意的是 match 函数是从字符串的开头开始匹配,一旦出现没有匹配成功的时候,就立即返回,不会再从失败的为止重新匹配pattern,举个栗子

import re
if __name__ == "__main__":
    source = "wellwellhold123"
    m1 =  re.match(r"wellhold\w+", source)
    if m1:
        print "this is m2 : " + m1.group(0)
    else:
        print "m1 is None"
    m2 = re.match(r"well\w+", source)
    if m2:
        print "this is m2 : " + m2.group(0)
    else:
        print " m2 is None"

输出结果

m1 is None
this is m2 : wellwellhold123

从输出结果可以看到,m1 在匹配的时候,先匹配到 well之后发现字符不对,就立马返回了,而并不会从well之后的 w继续重新匹配。

MatchObject 相关函数与属性

在上面那段代码里,我们看到了新的东西,没错,那就是 .group( ) 函数,顾名思义,这个就是表示获取匹配到的 MatchObject 当中的组,下面我们通过一个栗子来看看group的使用方法和强大之处。

import re
if __name__ == "__main__":
    source = "wellhold : 9527 , well : 1046"
    m1 =  re.match(r"(?P<name>[a-z]+) : (?P<workNum>\d+)", source)
    if m1:
        print "group0 : " + m1.group(0)
        print "group1 : " + m1.group(1)
        print "group2 : " + m1.group(2)
        print "group2 : " + m1.group("name")
        print "group2 : " + m1.group("workNum")
    else:
        print "m1 is None"

输出结果

group0 : wellhold : 9527
group1 : wellhold
group2 : 9527
group2 : wellhold
group2 : 9527

可以看到,group编号为0表示的是正则表达式本身匹配到的内容,而编号为1则表示在正则表达式中第一个组匹配的内容,2则表示表达式第二个组匹配到的内容,并且除此之外,如果使用的是命名组,还可以通过名字去获取该组获取到的内容,栗子中用的就是"name"和"workNum"去获取的这两个组的内容。
下表列举了包括了group方法之外,与match配合使用的其他函数方法:

函数名描述
group([index|id])获取匹配的组,缺省返回组 0
groups()返回全部的组,返回类型 tuple
groupdict()返回以组名为 key ,匹配的内容为 values 的字典,返回类型 dict
start( [group] )获取匹配的组的开始位置,返回类型 int
end( [group] )获取匹配的组的结束位置,返回类型 int
span( [group] )获取匹配的组的(开始,结束)位置,返回类型 tuple
expand( template )根据一个模版用找到的内容替换模版里的相应位置,返回类型string
import json
import re
if __name__ == "__main__":
    source = "wellhold:9527 ,well:1046"
    m1 =  re.match(r"(?P<name>[a-z]+):(?P<workNum>\d+)", source)
    if m1:
        print "group() : " + m1.group(0)
        print "groups() : %s " % str(m1.groups())
        print "groupdict() : %s " % json.dumps(m1.groupdict())
        print "start() : %s " % m1.start("name")
        print "end() : %s " % m1.end("workNum")
        print "span() : %s " % str(m1.span("name"))
        print "exspand() : %s " % m1.expand("name is \g<name> , workNumber is \g<workNum>")
    else:
        print "m1 is None"

输出内容:

group() : wellhold:9527
groups() : ('wellhold', '9527') 
groupdict() : {"name": "wellhold", "workNum": "9527"} 
start() : 0 
end() : 13 
span() : (0, 8) 
exspand() : name is wellhold , workNumber is 9527 

下表列出了除了上述函数方法之外,MatchOject还有的一些属性

属性名描述
pos搜索开始的下标
endpos搜索结束的下标
lastindex匹配的最后一个组的序号
lastgroup匹配的最后组的名称
import re
if __name__ == "__main__":
    source = "wellhold:9527 ,well:1046"
    m1 =  re.match(r"(?P<name>[a-z]+):(?P<workNum>\d+)", source)
    if m1:
        print "pos : %s" % m1.pos
        print "endpos : %s " % m1.endpos
        print "lastindex : %s " % m1.lastindex
        print "lastgroup : %s " % m1.lastgroup
    else:
        print "m1 is None"

输出结果

pos : 0
endpos : 24 
lastindex : 2 
lastgroup : workNum 

re.search(pattern,source [ , flag ])

这个方法和之前说的match方法有些类似,但是唯一不同的地方在于,search函数在匹配失败的时候,会从失败的地方重新再开始匹配表达式,直到到达source尾部还未匹配到符合表达式的字符串才返回,返回的类型为:若成功返回一个 Match 对象,失败无返回,举个栗子

import re
if __name__ == "__main__":
    source = "wellwellhold123"
    s1 =  re.search(r"wellhold\w+", source)
    if s1:
        print "this is m2 : " + s1.group(0)
    else:
        print "m1 is None"
    s2 = re.search(r"well\w+", source)
    if s2:
        print "this is m2 : " + s2.group(0)
    else:
        print " m2 is None"

输出结果

this is m2 : wellhold123
this is m2 : wellwellhold123

re.finditer(pattern,source [ , flag ])

这个函数与findall有点类似,但是相比findall却更为好用,可以看做是findall的加强版,因为它返回的是一个MatchObject的迭代器,可以直接通过遍历这个迭代器对匹配到的所有内容进行逐个逻辑处理,多说无益,举个栗子

import re
if __name__ == "__main__":
    source = "well wellhold wellon"
    for i in re.finditer(r'\w+',source):
        print i.group(),i.span()

输出结果:

well (0, 4)
wellhold (5, 13)
wellon (14, 20)

re.sub(pattern , replace , source [ , count ])

这个函数方法主要是进行字符串的替换和修改,并且相比String类型自带的函数功能更为强大,因为可以通过使用正则表达式去进行匹配。
而后边的可选参数 count ,则是可以指定的最多替换的次数,举个栗子

import re
if __name__ == "__main__":
    source = "wellhold used to have 1000 money , but now he just have 100 money "
    print re.sub("\d+","10000",source)

输出结果

wellhold used to have 10000 money , but now he just have 10000 money 

re.subn(pattern , replace , source [ , count ])

这个函数可以看做是sub的加强方法,用来获取字符串处理的一些数据,返回一个元组,除了第一个元素是被替换的字符串,还有第二个元素,表明产生了多少次替换。

import re
if __name__ == "__main__":
    source = "wellhold used to have 1000 money , but now he just have 100 money "
    print re.subn("\d+","10000",source)

输出结果

('wellhold used to have 10000 money , but now he just have 10000 money ', 2)

re.split(pattern , source [, maxNum])

切片函数相信大家都不陌生,相比String提供的split,这里的split也功能更强大,通过下面一个栗子,大家就知道强大在哪了

import re
if __name__ == "__main__":
    source = "wellhold used to have 1000 money  ,    but now he just have 100 money"
    print re.split( '\s*,\s*',source)

输出结果

['wellhold used to have 1000 money', 'but now he just have 100 money']

可以看到如果我们使用string提供的.split(),只能将两个字符串分开,但是,左右的空格是还需要进行额外处理的,但是使用正则表达式提供的split,可以通过正则表达式一步到位,效果极佳

深入了解正则表达式的使用

通过上面这么多的讲解,我们对于正则表达式的基础运用,应该是有所了解了,那么接下来我们再补充一点性能方面的东西,如何才能让我们写出来的正则表达式跑的更快呢?
答案就是使用编译好的Pattern 对象,简单的解释一下,我们在前面的栗子当中,用的全是在re模块的函数里头编写的正则表达式,那么如果需要重复使用多次,那么每次都需要重新编译re中的正则表达式,次数多的话,那么会导致速度慢下来很多,这时候我们时候一个pattern对象对需要重复使用的正则表达式进行一次编译,多次使用,可以节省多次编译的时间,而且在pattern对象中封装的方法,也相比re中的方法要快一些和多一些功能,那么pattern对象给我们提供了些什么方法呢?没错,答案就是之前说的re模块的所有方法,都在pattern中进行了重新封装,主要是参数进行了重新封装:

findall ( source[, startPos [,endPos] ] )
finditer ( source[, startPos [,endPos] ] )
match ( source[, startPos [,endPos] ] )
search ( source[, startPos [,endPos] ] )

即在方法中,不需要再指定正则规则,多说无益,举个栗子

import re
if __name__ == "__main__":
    source = "wellhold:9527 ,well:1046"
    p1 = re.compile("(?P<name>[a-z]+):(?P<workNum>\d+)")
    m1 = p1.match(source)
    if m1:
        print "pos : %s" % m1.pos
        print "endpos : %s " % m1.endpos
        print "lastindex : %s " % m1.lastindex
        print "lastgroup : %s " % m1.lastgroup
    else:
        print "m1 is None"

输出结果

pos : 0
endpos : 24 
lastindex : 2 
lastgroup : workNum 

从代码中看到,一个pattern实例是通过re.compile进行构成的,之后的使用方法其实与re模块内的方法没有什么大的不同。
最后补充一些pattern对象的属性

属性描述
flags查看编译时选择的模式
pattern查看编译的正则规则
groupindex规则里的组的下表

举个栗子

import re
if __name__ == "__main__":
    source = "wellhold:9527 ,well:1046"
    p1 = re.compile("(?P<name>[a-z]+):(?P<workNum>\d+)",re.S)
    print "pattern : %s " % p1.pattern
    print "flags : %s " % p1.flags
    print "groupindex : %s " % p1.groupindex

输出结果

pattern : (?P<name>[a-z]+):(?P<workNum>\d+) 
flags : 16 
groupindex : {'name': 1, 'workNum': 2} 

结语

到这里,我们的正则表达式的基础讲解也就记录完毕了,如果有哪位读者能读到这,那就说明你也是一个对正则表达式有很浓厚兴趣的人,希望这篇学习笔记能够给你带来一些东西,笔者也是初学,所以有什么错误的地方或者理解不对的地方,还请大神能评论留言指点一下,除此之外,如果觉得这篇博客还可以的,还希望有兴趣的读者能够来个“素质三连”,点赞留言收藏~
最后致谢一下本篇博客有参考和引用的部分内容的一些博客和相应出处:
https://www.cnblogs.com/dreamer-fish/p/5282679.html

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值