详解Python中的正则表达式的用法,python正则表达式


一、简介

       正则表达式(RE)为高级文本模式匹配,以及搜索、替代等功能提供了基础。正则表达式是一些由字符和特殊符号组成的字符串,它们描述了这些字符和字符的某种重复方式,因此能按某种模式匹配一个有相似特征的字符串的集合,因此能按某模式匹配一系列有相似特征的字符串。
二、详解
1、正则表达式使用的特殊符号和字符

       正则表达式最常用的元字符(metacharacters):特殊字符和符号,正是它们赋予了正则表达式强大的功能和灵活性。
    

201549145723307.jpg (476×756)

       (1)用管道符号( | )匹配多个正则表达式模式
        管道符号( | )表示一个或操作,它的意思是选择被管道符号分隔的多个不同的正则表达式中的一个。它可以匹配不止一个字符串,“或”(操作)有时候也被叫做“联合”(union)或者逻辑或(OR)。

201549145756510.png (302×80)

 (2)匹配任意一个单个的字符( . )
        点字符或句号(.)符号匹配除换行符(NEWLINE)外的任意一个单个字符(Python的正则表达式有一个编译标识 [S or DOTALL],该标识能去掉这一限制,使 ( . )在匹配时包括换行符。无论是字母、数字、不包括“\n”的空白符、可打印的字符、还是非打印字符或是一个符号,( . )都可以匹配它们。匹配一个点号(.)本身,必须(在前面)使用反斜线“\”对它进行转义。

201549145845308.png (492×94)

 (3)从字符串的开头或结尾或单词边界开始匹配( ^、$、\b、\B )
        还有些符号和特殊字符是用来从字符串的开头或结尾开始搜索正则表达式模式的,从字符串的开头开始匹配一个模式用字符^或特殊字符\A,匹配字符串的结尾用美元符号$或\Z,若想匹配这两个字符中的任何一个必须用反斜线进行转义。
       特殊字符\b and \B用来匹配单词边界。两者之间的区别是\b匹配的模式是一个单词边界,即与之对应的模式一定在一个单词的开头,不论这个单词的前面是有字符(该词在一个字符串的中间),还是没有字符(该单词在一行的起始处)。同样地,\B只匹配出现在一个单词中间的模式(即不在单词边界上的字符)。
 

201549145913857.png (453×110)

       (4)创建字符类( [ ] )
           尽管点号可用来匹配任意字符,但有时候需要匹配某些个特殊的字符。正因为如此,方括号( [ ] )被发明出来。使用方括号的正则表达式会匹配方括号里的任何一个字符。

201549150410370.png (672×113)

    (5)指定范围 ( - ) 和 否定( ^ )
       方括号除匹配单个字符外,还可以支持所指定的字符范围。方括号里一对符号中间的连字符(-)用来表示一个字符的范围,例如A–Z、a–z或0–9分别代表大写字母、小写字母和十进制数字。这是一个按字母顺序排序的范围,所以它不限于只用在字母和十进制数字上。另外,如果在左方括号后第一个字符是上箭头符号(^),就表示不匹配指定字符集里的任意字符。
    

201549150536342.png (669×228)

       6)使用闭包操作符( *、+、?、{} )实现多次出现/重复匹配
        特殊符号“*”、“+”和“?”,它们可以用于匹配字符串模式出现一次、多次、或未出现的情况。星号或称星号操作符匹配它左边那个正则表达式出现零次或零次以上的情况。加号(+)操作符匹配它左边那个正则表达式模式至少出现一次的情况,而问号操作符( ? )匹配它左边那个正则表达式模式出现零次或一次的情况。
        花括号操作符({ }),花括号里可以是单个的值,也可以是由逗号分开的一对值。如果是一个值,如{N}则表示匹配N次出现;如果是一对值,即{M, N}就表示匹配M次到N次出现。可以在这些符号前用反斜线进行转义,使它们失去特殊作用,即“\*”将匹配星号本身等。使用了表示重复的元字符(*+?{m,n})时,正则表达式引擎在匹配模式时会尽量"吸收"更多的字符,这就叫做"贪心"。问号告诉正则表达式引擎尽可能地偷懒,要求当前匹配消耗的字符越少越好,留下尽可能多的字符给后面的模式。

201549150246912.png (671×294)

 (7)特殊字符表示字符集
       可以不使用“0–9”这个范围表示十进制数字,而改用简写“\d”表示。另一个特殊的字符“\w”可用来表示整个字符数字的字符集,即相当于“A-Za-z0-9_”的简写形式,特殊字符“\s”代表空白字符。这些特殊字符的大写形式表示不匹配,比如“\D”表示非十进制数字的字符(等价于 “[^0-9]”)等等。
     

201549150614560.png (668×159)

       (8)用圆括号(()) 组建组
       对圆括号(())和正则表达式一起使用时可以实现任意一个(或两个)功能:对正则表达式进行分组、匹配子组。
       使用圆括号的一个额外好处就是匹配的子串会被保存到一个子组,便于今后使用。这些子组可以在同一次匹配或搜索中被重复调用,或被提取出来做进一步处理。
      

201549150711389.png (678×179)

2、正则表达式和Python
        re引擎已在Python1.6版本中被重写,改进了它的性能并添加了对 Unicode 的支持。接口并没有改变,因此模块的名字也保持不变。新的re引擎,内部被叫做sre。
(1)re模块: 核心函数和方法
       常见的正则表达式函数与方法:

201549150849250.png (554×117)

201549150800085.png (564×176)

       (2)使用compile()编译正则表达式

        Python的代码最终会被编译为字节码,然后才被解释器执行。我们提到用调用eval()或exec()调用一个代码对象而不是一个字符串,在性能上会有明显地提升,是因为对前者来说,编译过程不必执行。即使用预编译代码对象要比使用字符串快,因为解释器在执行字符串形式的代码前必须先把它编译成代码对象。这个概念也适用于正则表达式,在模式匹配之前,正则表达式模式必须先被编译成regex对象。若正则表达式在执行过程中被多次用于比较,建议先对它做预编译,而且既然正则表达式的编译是必须的,那使用么预先编译来提升执行性能无疑是明智之举。re.compile()就是用来提供此功能的。其实模块函数会对已编译对象进行缓存,所以不是所有使用相同正则表达式模式的search()和match()都需要编译,即使这样,仍然节省了查询缓存和用相同的字符串反复调用函数的性能开销。
(3)匹配对象和group()、groups()方法
        在处理正则表达式时,除regex对象外,还有另一种对象类型-匹配对象。这些对象是在match()或search()被成功调用之后所返回的结果。匹配对象有两个主要方法:group()和groups()。
         group()方法或者返回所有匹配对象或是根据要求返回某个特定子组。groups()则很简单,它返回一个包含唯一或所有子组的元组。如果正则表达式中没有子组的话, groups()将返回一个空元组,而group()仍会返回全部匹配对象。
(4)用match()匹配字符串
        match()函数尝试从字符串的开头开始对模式进行匹配,如果匹配成功就返回一个匹配对象;如果匹配失败了,就返回None。匹配对象的group()方法可以用来显示那个成功的匹配。
在CODE上查看代码片派生到我的代码片

  >>> m = re.match('foo', 'foo') # pattern matches string 
  >>> if m is not None: # show match if successful  
  ...   m.group() 
  ...  
  'foo' 
  >>> m = re.match('foo', 'bar') 
  >>> if m is not None: 
  ...   m.group() 
  ...  

      m是一个匹配对象的实例,即使字符串比模式要长,匹配也可能成功。如果匹配失败,会引发一个AttributeError异常。
在CODE上查看代码片派生到我的代码片

  >>> re.match('foo', 'food on the table').group() 
  'foo' 
  >>> re.match('foo', 'fod on the table').group() 
  Traceback (most recent call last): 
   File "<stdin>", line 1, in <module> 
  AttributeError: 'NoneType' object has no attribute 'group' 

(5)search() 在一个字符串中查找一个模式
        search和match的工作方式一样,不同之处在于search会检查参数字符串任意位置的地方给定正则表达式模式的匹配情况。如果搜索到成功的匹配,会返回一个匹配对象,否则返回None。match()尝试从字符串起始处进行匹配模式,而search()查找字符串中模式首次出现的位置,而不是尝试(在起始处)匹配,严格地说search()是从左到右进行搜索。
[html] view plaincopy在CODE上查看代码片派生到我的代码片

  >>> m = re.search('foo', 'seafood') 
  >>> if m is not None: m.group() 
  ...  
  'foo' 

       regex对象的方法match()和 search()与匹配对象的方法group()、groups()配合使用,共同处理正则表达式语法中的绝大多数特殊字符和符号。
(6)匹配多个字符串( | )
在CODE上查看代码片派生到我的代码片

  >>> bt = 'bat|bet|bit' # RE pattern: bat, bet, bit 
  >>> m = re.match(bt, 'bat') 
  >>> if m is not None: m.group() 
  ...  
  'bat' 
  >>> m = re.search(bt, 'He bit me!') 
  >>> if m is not None: m.group() 
  ...  
  'bit' 

(7)匹配任意单个字符( . )
       点号是不能匹配换行符或非字符(即空字符串)的,在正则表达式中用反斜线对它进行转义,使点号失去它的特殊意义。
在CODE上查看代码片派生到我的代码片

  >>> anyend = '.end' 
  >>> m = re.match(anyend, 'bend') # dot matches 'b' 
  >>> if m is not None: m.group() 
  ...  
  'bend' 
  >>> m = re.match(anyend, 'end') # no char to match 
  >>> if m is not None: m.group() 
  ...  
  >>> m = re.match(anyend, '\nend')  # any char except \n 
  >>> if m is not None: m.group() 
  ...  
  >>> m = re.search('.end', 'The end.')# matches ' ' in search . 
  >>> if m is not None: m.group() 
  ...  
  ' end' 
  >>> pi_patt = '3\.14' 
  >>> m = re.match(pi_patt, '3.14') 
  >>> if m is not None: m.group() 
  ...  
  '3.14' 
  >>> m = re.match(pi_patt, '3014') 
  >>> if m is not None: m.group() 
  ...  

(8)创建字符集合( [ ] )
在CODE上查看代码片派生到我的代码片

  >>> m = re.match('[cr][23][dp][o2]', 'c3po')# matches 'c3po' 
  >>> if m is not None: m.group() 
  ...  
  'c3po' 
  >>> m = re.match('r2d2|c3po', 'c2do')# does not match 'c2do' 
  >>> if m is not None: m.group() 
  ...  
  >>> m = re.match('r2d2|c3po', 'r2d2')# matches 'r2d2' 
  >>> if m is not None: m.group() 
  ...  
  'r2d2' 

(9)重复、特殊字符和子组
        简单电子邮件地址的正则表达式(“\w+@\w+\.com”),或许想要匹配的邮件地址比这个正则表达式的允许的要多。
在CODE上查看代码片派生到我的代码片

  >>> patt = '\w+@(\w+\.)?\w+\.com' 
  >>> re.match(patt, 'nobody@xxx.com').group() 
  'nobody@xxx.com' 
  >>> re.match(patt, 'nobody@www.xxx.com').group() 
  'nobody@www.xxx.com' 
  >>> patt = '\w+@(\w+\.)*\w+\.com' 
  >>> re.match(patt, 'nobody@www.xxx.yyy.zzz.com').group() 
  'nobody@www.xxx.yyy.zzz.com' 

       如何用group()方法访问每个子组以及用groups()方法获取一个包含所有匹配子组的元组:
在CODE上查看代码片派生到我的代码片

  >>> m = re.match('(\w\w\w)-(\d\d\d)', 'abc-123') 
  >>> m.group() 
  'abc-123' 
  >>> m.groups() 
  ('abc', '123') 
  >>> m.group(1) 
  'abc' 
  >>> m.group(2) 
  '123' 
  >>> m = re.match('(a)(b)', 'ab') 
  >>> m.groups() 
  ('a', 'b') 

(10)从字符串的开头或结尾匹配及在单词边界上的匹配
在CODE上查看代码片派生到我的代码片

  >>> m = re.search('^The', 'end. The') # not at beginning 
  >>> if m is not None: m.group() 
  ...  
  >>> m = re.search(r'\bthe', 'bite the dog') # at a boundary 
  >>> if m is not None: m.group() 
  ...  
  'the' 
  >>> m = re.search(r'\bthe', 'bitethe dog') # no boundary 
  >>>  
  >>> if m is not None: m.group() 
  ...  
  >>> m = re.search(r'\Bthe', 'bitethe dog') # no boundary 
  >>> if m is not None: m.group() 
  ...  
  'the' 

(11)用findall()找到每个出现的匹配部分
        findall()用于非重叠地查找某字符串中一个正则表达式模式出现的情况,findall()和search()相似之处在于二者都执行字符串搜索,不同之处是findall()总返回一个列表。如果findall()没有找到匹配的部分,会返回空列表;如果成功找到匹配部分,则返回所有匹配部分的列表(按从左到右出现的顺序排列)。
[html] view plaincopy在CODE上查看代码片派生到我的代码片

  >>> re.findall('car', 'carry the barcardi to the car') 
  ['car', 'car', 'car'] 
  >>> re.findall('car', 'scary') 
  ['car'] 
  >>> re.findall('car', 'ssary') 
  [] 

        当正则表达式仅有一个子组时,findall()返回子组匹配的字符串组成的列表;如果表达式有多个子组,返回的结果是一个元组的列表,元组中每个元素都是一个子组的匹配内容,像这样的元组(每一个成功的匹配对应一个元组)构成了返回列表中的元素。
(12)用 sub()[和 subn()]进行搜索和替换
       sub()和subn()都是将某字符串中所有匹配正则表达式模式的部分进行替换。用来替换的部分通常是一个字符串,但也可能是一个函数,该函数返回一个用来替换的字符串。subn()和sub()一样,但它还返回一个表示替换次数的数字,替换后的字符串和表示替换次数的数字作为一个元组的元素返回。
在CODE上查看代码片派生到我的代码片

  >>> re.sub('[ae]', 'X', 'abcdef') 
  'XbcdXf' 
  >>> re.subn('[ae]', 'X', 'abcdef') 
  ('XbcdXf', 2) 

(13)用 split()分割(分隔模式)
        如果分隔符没有使用由特殊符号表示的正则表达式来匹配多个模 式,那re.split()和string.split()的执行过程是一样的。
在CODE上查看代码片派生到我的代码片

  >>> re.split(':', 'str1:str2:str3') 
  ['str1', 'str2', 'str3'] 

Linux下who命令输出结果进行分隔:
在CODE上查看代码片派生到我的代码片

  #!/usr/bin/env python 
   
  from os import popen 
  from re import split 
   
  f = popen('who', 'r') 
  for eachLine in f: 
    print split('\s\s+|\t', eachLine.strip()) 
  f.close() 

在CODE上查看代码片派生到我的代码片

  ['aoyang', 'tty1', '2015-03-27 09:06 (:0)'] 
  ['aoyang', 'pts/0', '2015-03-27 09:09 (:0.0)'] 
  ['aoyang', 'pts/1', '2015-03-27 11:41 (:0.0)'] 
  ['aoyang', 'pts/2', '2015-03-27 14:37 (:0.0)'] 

       保存用户的登录信息,用string.split()很难有效果,因为分隔这些数据的空白符号是毫无规律且不确定的,正则表达式很容易做到这一点。用os.popen()执行who命令,去掉每行行尾的换行符(NEWLINE),并添加检查单个TAB符号的模式,把TAB做为re.split()的可选分隔符。
        ASCII 字符和正则表达式特殊字符间所产生的冲突。比如特殊符号“\b”在ASCII字符中代表退格键,但同时“\b”也是一个正则表达式的特殊符号,“匹配一个单词边界”代表。为了让RE编译器把两个字符“\b”当成想要表达的字符串,而不是一个退格键,需要用另一个反斜线对它进行转义,即可以这样写:“\\b”。
三、总结
(1)正则表达式用于处理字符串的强大工具,拥有自己独特的语法以及一个独立的处理引擎,效率上可能不如str自带的方法,但功能十分强大,能完成模式匹配、提取和搜索-替换等功能。
(2)在此仅给出常用的符号,还需在实战中积累正则表达式的用法。
(3)若有不足,请留言,在此先感谢!
      

评论关闭