如何在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对象

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,961评论 5 473
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,444评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,009评论 0 333
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,082评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,101评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,271评论 1 278
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,738评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,395评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,539评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,434评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,481评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,160评论 3 317
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,749评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,816评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,038评论 1 256
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,548评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,140评论 2 341

推荐阅读更多精彩内容