如何在python中使用正则表达式

前一段用python写了个简单的页面抓取程序,其中使用了正则表达式,也重新学习相关的正则表达式知识.
主要从三个方面来理解:

  • 正则表达式的基本知识
  • 如何学习正则表达式
  • python语言如何使用正则表达式

正则表达式的基本知识

正则表达式分为二种标准PCREPOSFIX
PCRE主要得益于Perl语言的发展,现在PHPPython实现正则也是基于PCRE

POSIX把正则表达式分为两种:BRE(Basic Regular Expressions)与ERE(Extended Regular Expressions).现在的GUN软件对这二种标准做了很大程度的兼容,功能基本相同,只是语法不同

下面这张图很好的反应了正则表达式的特点和功能,以及BREERE的比较
![posix-regexp-favor][201608-reg-1]
[201608-reg-1]: http://notes.newyingyong.cn/static/image/2016/201608-reg-1.png “posix-regexp-favor”

在使用awk,shell,sed,vim的时候要注意区别对待BREERE

如何学习正则表达式

  • 学习正则表达式没有任何技巧,需要不断的实践
  • 通过系统性的书去学习正则表达式,而不是随便从网上寻找对应知识,完整的了解正则表达式,包括不同表达式的流派,推荐书籍正则表达式必知必会
  • 尝试理解不同语言实现正则的设计理念,比如vim,python,php
  • 正则表达式虽然很容易实现,但是准确性很重要,实现匹配很简单,但是如何排除不想匹配到的则比较困难

python语言如何使用正则表达式

关于反斜杠

python正则表达式中通过反斜杠符号取消元字符的特殊含义,这和字符串的反斜杠符号产生了冲突
比如为了匹配字符串\str,可能需要这么写

p = re.compile('\\\\str');
m = p.match('\strok') 
if m :
    print m.group()

\\str:为 re.compile 取消反斜杠的特殊意义
\\\\str:为 \\section 的字符串实值取消反斜杠的特殊意义

为解决该问题,可以使用raw字符串表示(‘r’).

p = re.compile(r"\\str") 
m = p.match('\strok') 
if m :
    print m.group()

关于贪婪

正则匹配默认是贪婪的,会试着重复尽可能多的次数,通过?符号可以修改为非贪婪模式

p = re.compile('<.*?>')
m = p.match('<html><head><title>Title</title>')
if m :
    print m.group()

编译标志

通过编译标志可以修改正则表达式的一些运行方式

p = re.compile(r'str.*str' ,re.DOTALL|re.IGNORECASE )
m = p.search("str\nstR")
if m :
    print m.group()

re.DOTALL:使.特殊字符完全匹配任何字符,包括换行.
re.IGNORECASE:忽略大小写

假如没有设置MULTILINE标志,^它只是匹配字符串的开始,而在MULTILINE模式里,^也可以直接匹配字符串中的每个换行。

另外 match() 函数决定 RE 是否在字符串刚开始的位置匹配。

比较下面的代码:

print(re.findall('(hello.$)', 'hello1\nhello2\n))
print(re.findall('(hello.$)', hello1\nhello2\n',re.MULTILINE))

Match对象

在说Match对象之前,先简单说下分组.
分组就是对于匹配出来的内容再进一步分隔,以便获取到更精准的内容.在Python中通过
()表示分组.通过Match对象能够方便返回各个分组的内容.
个人理解在Python中分组和Match对象精密关联的

match()search()匹配模式的时候返回Match对象,否则返回None
Match对象的几个方法和属性,比如group(),groups(),span()

group()调用等同于group(0),返回整个匹配的字串

p = re.compile("\d+")
m = p.search("123,abc,456")
if m :
    print m.group()
    print m.group(0)

groups()返回包含子分组的元祖

p = re.compile("(\d+,)(\D+)")
m = p.search("123,abc")
if m :
    print m.group()
    print m.groups()
    print m.span(0)
    print m.span(2)
    print m.group(1,2)
    print m.start()

无捕获组

精心设计的 REs 也许会用很多组,既可以捕获感兴趣的子串,又可以分组和结构化 RE 本身。在复杂的 REs 里,追踪组号变得困难。

假如想用一个组去收集正则表达式的一部分,但又对组的内容不感兴趣。可以用一个无捕获组: (?:…) 来实现这项功能。

比较下面的代码就能明白含义:

p = re.compile("(abc){2}")
m = p.search("abcabcdef")
if m :
    print m.group()
    print m.groups()
p = re.compile("(?:abc){2}")
m = p.search("abcabcdef")
if m :
    print m.group()
    print m.groups()

除了捕获匹配组的内容之外,无捕获组与捕获组表现完全一样;
可以在其中放置任何字符,可以用重复元字符如 “*” 来重复它,可以在其他组(无捕获组与捕获组)中嵌套它。(?:…) 对于修改已有组尤其有用,因为可以不用改变所有其他组号的情况下添加一个新组。

命名组

分组中,更重要和强大的是命名组;与用数字指定组不同的是,它可以用名字来指定。

命令组的语法是 Python 专用扩展之一: (?P<name>…)。名字很明显是组的名字。除了该组有个名字之外,命名组也同捕获组是相同的。

举个例子:

p = re.compile(r'(?P<word>\b\w+\b),\1')
m = p.match('lot,lot,cd') 
if m:
    print m.group('word')

逆向引用

逆向引用可以使用 (...)\1 这样的方式,也可以使用命名组逆向引用(主要用到的 Python 扩展就是 (?P=name) ,它可以使叫 name 的组内容再次在当前位置发现)。举个例子:

p = re.compile(r'(?P<word>\b\w+\b),(?P=word)')
p2 = re.compile(r'(?P<word>\b\w+\b),\1')

m = p.match('lot,lot,cd')
if m:
    print m.group('word') 

m = p2.match('lot,lot,cd') 
if m:
    print m.group('word')

前后查找

在学习正则表达式的时候,前后查找是学的最辛苦的一部分,学习的时候要理解其含义

(?=...)表示假如匹配到模式,则返回...前面的内容,且返回的内容不包含...
(?<=...)表示假如匹配到模式,则返回...后面的内容,且返回的内容不包含...

p = re.compile('.*(?=:)')
print(p.search("http://www.baidu.com").group())

p = re.compile('(?<=://).*')
print(p.search("http://www.baidu.com").group())

p = re.compile('(?<=<title>).*(?=</title>)')
print(p.search("<title>hello</title>").group())

前后查找取非则有很多作用:

p = re.compile('.*[.](?!bat$).*$')
print(p.search("file.log").group())
print(p.search("file.bat").group())

搜索和替换

找到所有模式匹配的字符串并用不同的字符串来替换它们。sub() 方法提供一个替换值,可以是字符串或一个函数,和一个要被处理的字符串。可以使用 \g<name>\g<number> 等来进行替换。

p = re.compile(r'(?P<word>\b\w+\b)')
print(p.sub(r"\g<word>ok",'Lots of punctuation'))
print(p.sub(r"\g<1>ok",'Lots of punctuation'))
print(p.sub(r"\1ok",'Lots of punctuation'))

替换的模式还支持函数的方式:

def hexrepl( match ):
    value = int( match.group() )
    return hex(value)

p = re.compile(r'\d+')
print (p.sub(hexrepl, 'Call 65490 for printing'))

如果替换是个函数,该函数将会被模式中每一个不重复的匹配所调用。在每次调用时,函数会被传入一个 MatchObject 的对象作为参数,因此可以用这个对象去计算出替换字符串并返回它

字符串分片

通过正则表达式将字符串分片。如果捕获括号在 RE 中使用,那么它们的内容也会作为结果列表的一部分返回,比较下面的代码:

p = re.compile(r'\W+')
print(p.split('This is a test'))

p2 = re.compile(r'(\W+)')
print(p2.split('This is a test'))

Python 中其他 RE 对象

(1)RegexObject对象:构建正则表达式对象
(2)Match对象

    原文作者:虞大胆的叽叽喳喳
    原文地址: https://www.jianshu.com/p/b68f62d0ab6d
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞