Python2.7 - Regular Expression HOWTO 学习小结

说明

基本的用法

  • end() 的值是第一个不匹配的字符的下标,而不是最后一个匹配字符的下标
  • match()是从第一个字符开始匹配的,而不是可以从中间搜索;查到中间匹配的是search()。
p1 = re.compile('[a-z]+')
m1 = p1.match( '...tem1po' )
m2 = p1.match( 'tem1po' )

上面这段代码中:
m1的值是None,原因是match是从第一个字符开始匹配,而第一个字符无法匹配;
m2的值是'tem',原因是第一个字符是匹配的,然后一直到1这个不匹配的字符终止

  • search()只匹配到第一个匹配字符即终止
    跟match()相比较区别在于,match()是从第一个字符开始匹配,而search()是寻找到第一个可以匹配的字符。但是匹配模式加上^的话,效果是一样的。
p1 = re.compile('^[a-z]+')
m1 = p1.search( '...tem1po2asdasd' )
p2 = re.compile('[a-z]+')
m2 = p2.match( '...tem1po2asdasd' )

上面的例子中m1和m2的效果是一样的,都是None

  • raw字符串
    跟""有关系,对于re的compile来说,\是特殊字符。如果要匹配一个字符串中的空格边界,则需要输入\b才可以匹配上。
    如果使用raw字符串,就是在字符串前面加上r,则只需要输入\b即可。
p = re.compile('\.class\\b', 'no .class at all')
p1 = re.compile(r'\.class\b', 'no .class at all')

上面的例子中,P和P1是等效的。但是.为什么不受影响,没有想明白。
不管怎样,所有的pattern字符串加上r就好了

group & groups

还是拿代码直接演示比较好

  • 例子1
    代码:跟原始的示例代码相比,将固定的b修改成不同的数字
p = re.compile('(a[0-9]c)*')
m = p.match('a1ca2ca3c')
print m.group()
print m.group(0)
print m.group(1)
print m.groups()

运行结果

a1ca2ca3c
a1ca2ca3c
a3c
('a3c',)

从这个结果可以看到:

  1. 匹配的原意是a和b中间夹一个数字,并且多次匹配,所以最终的匹配结果是整个字符串
  2. group()的结果和group(0)的结果是一样的
  3. 由于只有一对(),所以group的下标只到1,其输出结果就是捕获的一对();不过让人意外的是,输出的是最后一对,而不是第一对。
  4. groups()输出的是个元组,只存放捕获的();值就是group(0)以后的数据
  • 例子2
    代码:跟原始的例子相比,多了1对额外的(),并且将所有的group下标都打印出来
p = re.compile(r'(((a)[0-9])c)*(dfg)')
m = p.match('a1ca2ca3cdfg')
print m.group()
print m.group(0)
print m.group(1)
print m.group(2)
print m.group(3)
print m.group(4)
print m.groups()

运行结果

a1ca2ca3cdfg
a1ca2ca3cdfg
a3c
a3
a
dfg
('a3c', 'a3', 'a', 'dfg')

这个例子跟第一个例子比起来:

  1. 有1对()变成了4对。其中左边的是3对嵌套的(),右边是单独的一对()
  2. 从输出的结果看,先左后右,先外后里的进行匹配输出
  • 例子3

代码1

p = re.compile(r'((\b\w+)\.+)\.+')
m = p.search('Paris..in..the..the..spring')
print m.group()
print m.group(0)
print m.group(1)
print m.group(2)
print m.groups()

运行结果1

Paris..
Paris..
Paris.
Paris
('Paris.', 'Paris')
  • **代码2 **
p = re.compile(r'((\b\w+)\.+)\.+\0')
m = p.search('Paris..in..the..the..spring')
print m.group()
print m.group(0)
print m.group(1)
print m.group(2)
print m.groups()

运行结果2

Traceback (most recent call last):
  File "testPython/test_main.py", line 7, in <module>
    print m.group()
AttributeError: 'NoneType' object has no attribute 'group'
  • 代码3
p = re.compile(r'((\b\w+)\.+)\.+\1')
m = p.search('Paris..in..the..the..spring')
print m.group()
print m.group(0)
print m.group(1)
print m.group(2)
print m.groups()

运行结果3

the..the.
the..the.
the.
the
('the.', 'the')
  • 代码4
p = re.compile(r'((\b\w+)\.+)\.+\2')
m = p.search('Paris..in..the..the..spring')
print m.group()
print m.group(0)
print m.group(1)
print m.group(2)
print m.groups()

运行结果4

the..the
the..the
the.
the
('the.', 'the')

说明

  1. 代码1是没有添加\1,就是常规的匹配。主要是查看常规匹配下的group的值
  2. 代码2是加上了\0,也就是group(0)。但是似乎不允许这样,group(0)实质就是group()本身。
    这样做似乎产生了一个递归,所以匹配结果就是None。第一次匹配结果是the..,加上\0后,\0本身的结果就变成了the..the..,然后再加一次本身。这样也能去解释匹配为0.
    仔细去观察4段代码的运行结果,gourp(1)和group(2)的结果一直都是一样的。
  3. 代码3和代码4,因为原始的匹配就是有2对(),所以可以使用不同的下标对应不同的。

Non-capturing and Named Groups

  • (?:...)
    这个符号的作用是,不取回...对应的值,放入group中。也就是说匹配上之后,根本就没有group。
p = re.compile(r'(?:[abc])+')
m = p.search('abc')
print m.group()
print m.group(0)
print m.group(1)
print m.groups()

上面这段代码的运行结果是在group(1)的时候抛出异常,因为这种模式下没有group。运行结果如下:

abc
abc
Traceback (most recent call last):
  File "test_main.py", line 9, in <module>
    print m.group(1)
IndexError: no such group

如果我们去掉group(1)的打印,只打印出groups的话,就能看出没有group。代码如下:

p = re.compile(r'(?:[abc])+')
m = p.search('abc')
print m.group()
print m.group(0)
print m.groups()

运行结果是:

abc
abc
()

可以明显的看出groups是没有任何值的。

  • (?P<name>...)
    这个是Python特定的一个扩展实现,其实就是给...表达式匹配的值设定了一个key值,然后可以使用(?P=name)来调用之前表达式匹配的值。看下面的例子,就能理解了。
p = re.compile(r'(?P<word>\b\w+)\s+(?P=word)')
m = p.search('Paris in the the spring')
print m.group()
print m.group(0)
print m.group(1)
print m.groups()

上面的代码运行结果是:

the the
the the
the
('the',)

仔细观察表达式,并根据原理,我们可以看到(?P=word)等同于group的\1回调。\1是在group中找到1为下标的值,而(?P=word)则是通过word这个key值找到对应的匹配值。

  • (?=...)
    这个看起来和(?:...)很像,但是其实不是一回事。还是用例子说话吧
print u"============= 例子1  =============="
m = re.search(r"(?:[abc])+", "abc")
print m
print m.group()
print m.groups()
print u"============= 例子2  =============="
m = re.search(r"(?=[abc])+", "abc")
print m
print m.group()
print m.groups()
print u"============= 例子3  =============="
m = re.search(r"(?=[abc])+", "1abc")
print m
print m.group()
print m.groups()
print u"============= 例子4  =============="
m = re.search(r"\d(?=[abc])+", "1abc")
print m
print m.group()
print m.groups()
print u"============= 例子5  =============="
m = re.search(r"\d(?:[abc])+\d", "1abc2")
print m
print m.group()
print m.groups()
print u"============= 例子6  =============="
m = re.search(r"\d(?=[abc])+\d", "1abc2")
print m
print u"============= 例子7  =============="
m = re.search(r"\d(?=[abc])+", "1abc2")
print m
print m.group()
print m.groups()
print u"============= 例子8  =============="
m = re.search(r"\d(?=[abc])+", "abc")
print m

运行结果是:

============= 例子1  ==============
<_sre.SRE_Match object at 0x103674370>
abc
()
============= 例子2  ==============
<_sre.SRE_Match object at 0x1036743d8>  
结果为空
()
============= 例子3  ==============
<_sre.SRE_Match object at 0x103674370>
结果为空
()
============= 例子4  ==============
<_sre.SRE_Match object at 0x1036743d8>
1
()
============= 例子5  ==============
<_sre.SRE_Match object at 0x10e9133d8>
1abc2
()
============= 例子6  ==============
None
============= 例子7  ==============
<_sre.SRE_Match object at 0x10e9133d8>
1
()
============= 例子8  ==============
None

上面的运行结果,可以得出如下的内容:

  1. (?=...)的匹配结果也是不会记录在group中的
  2. 例子2的结果,说明其实是匹配上了的,但是奇怪的是group()的结果却没有匹配值
  3. 例子3就在要匹配的字符串前加上数字1,发现还是没有输出结果
  4. 最后修改了表达式,在之前增加了\d以匹配例子3增加的数字1,结果发现是有输出的,输出结果就是\d匹配的1。
  5. 看到例子4之后,就在考虑如果后面增加匹配呢?
    首先例子5在+后面增加了一个\d的匹配,并在比对的字符串中再最后增加了2。例子5用:替换了=,从输出结果看,匹配是成功的。
  6. 例子6将例子5的:再替换回=,我们发现匹配是失败的,返回的对象是None
  7. 在例子7中,我们将匹配的表达式中在例子5增加的哪个/d再取消,匹配成功,返回值跟例子4一样。
  8. 例子把是在例子4基础上做修改,将比对的字符串的1去掉。从结果看,可以确定是先匹配整个表达式,而不仅仅是先匹配...

综合上面的实例,我们可以对(?=...)得出如下总结:
** 1. 此匹配表达式只能放在最后,针对()单元的+等重复标识除外**
** 2. 首先是匹配整个表达式,而不仅仅是...。在例子8中,就是先匹配r"\d(?=[abc])+"。 ** 3. 当整个表达式匹配成功后,其会找到...的前一个表达式,再从...匹配的字符往前看其前一个表达式的匹配的值,并作为最后输出。如果没有,则为空。在我们的例子中前一个表达式是/d,匹配的值是1`。**

Modifying Strings

  • ** split()**
    1. Split的参数是分割的标识,还可以加上最多分成几组的参数。
    2. 分割的结果是,除开被匹配上的字符,其余的字符组成结果
    3. 结果的最后一个,固定是空字符串
    4. 比较特殊的是,分割匹配包含在()中的话,输出的结果也包含匹配的字符串。
      直接引用官方文档的例子,就完全可以说明了
>>> p = re.compile(r'\W+')
>>> p.split('This is a test, short and sweet, of split().')
['This', 'is', 'a', 'test', 'short', 'and', 'sweet', 'of', 'split', '']
>>> p.split('This is a test, short and sweet, of split().', 3)
['This', 'is', 'a', 'test, short and sweet, of split().']
>>> p = re.compile(r'\W+')
>>> p2 = re.compile(r'(\W+)')
>>> p.split('This... is a test.')
['This', 'is', 'a', 'test', '']
>>> p2.split('This... is a test.')
['This', '... ', 'is', ' ', 'a', ' ', 'test', '.', '']
  • sub() & subn()
    sub和subn的区别在于,subn会输出执行替换的字符串的个数。其余的直接看官方列出的示例代码即可看明白。
>>> p = re.compile('(blue|white|red)')
>>> p.sub('colour', 'blue socks and red shoes')
'colour socks and colour shoes'
>>> p.sub('colour', 'blue socks and red shoes', count=1)
'colour socks and red shoes'
>>> p = re.compile('(blue|white|red)')
>>> p.subn('colour', 'blue socks and red shoes')
('colour socks and colour shoes', 2)
>>> p.subn('colour', 'no colours at all')
('no colours at all', 0)

有一个点是需要注意的,需要注意*号。先看下面的例子:

>>> p = re.compile('x*')
>>> p.sub('-', 'abxd')
'-a-b-d-'

上面例子中,由于*代表是可以0次,所以跟x隔开的空的地方,都被替换成了要替换的值。
如果是+,表示至少有一次时,上面的例子替换就显得正常了,见下面代码

>>> p = re.compile('x+')                     
>>> p.sub('-', 'abxd')
'ab-d'
  • 可以在替换函数中使用group下标 或者 p<name>
    直接看官方的代码示例就清楚了
>>> p = re.compile('section{ (?P<name> [^}]* ) }', re.VERBOSE)
>>> p.sub(r'subsection{\1}','section{First}')
'subsection{First}'
>>> p.sub(r'subsection{\g<1>}','section{First}')
'subsection{First}'
>>> p.sub(r'subsection{\g<name>}','section{First}')
'subsection{First}'
  • 可以在替换函数中调用其他函数
    下面是官方的代码,看起来有些递归的味道。函数的参数是match对象,而这个match对象实际上就是调用其函数的对象。
>>> def hexrepl(match):
...     "Return the hex string for a decimal number"
...     value = int(match.group())
...     return hex(value)
...
>>> p = re.compile(r'\d+')
>>> p.sub(hexrepl, 'Call 65490 for printing, 49152 for user code.')
'Call 0xffd2 for printing, 0xc000 for user code.'

Compilation Flags

  • IGNORECASE
    就是在匹配的时候不在乎大小写
    代码:
print u"============= 例子1  =============="
m = re.search(r"[abc]+", "aBc", re.IGNORECASE)
print m
print m.group()

运行结果:

============= 例子1  ==============
<_sre.SRE_Match object at 0x101eb8e68>
aBc
  • LOCALE
    根据官方文档的说法,\w只能匹配英文,类似于匹配了[a-zA-Z]。使用LOCALE则可以匹配更多本地字符。
    尝试了一下中文,似乎不行。由于没有其他例子,举得是法语字符,后续需要时再研究把。
  • MULTILINE
    ^$分别是从行首和行尾进行匹配,但是这个是没法跨行的。使用MULTILINE可以使得这两个跨行。
    代码:
print u"============= 例子1  =============="
m = re.findall(r"^[123]", "2abc\n1d")
print m
print u"============= 例子2  =============="
m = re.findall(r"^[123]", "2abc\n1d", re.MULTILINE)
print m

运行结果:

============= 例子1  ==============
['2']
============= 例子2  ==============
['2', '1']

从例子中,明显可以看出来,加了MULTILINE参数后,在新的一行^仍旧起了作用。

  • DOTALL
    配置之后,可以匹配上换行符;不配置,也就不能匹配。
    代码:
print u"============= 例子1  =============="
m = re.findall(r".+", "2abc\n1d")
print m
print u"============= 例子2  =============="
m = re.findall(r".+", "2abc\n1d", re.DOTALL)
print m

运行结果:

============= 例子1  ==============
['2abc', '1d']
============= 例子2  ==============
['2abc\n1d']
  • ** UNICODE**
    直接贴原文吧,涉及到再研究
Make \w, \W, \b, \B, \d, \D, \s and \S dependent on the Unicode character properties database.
  • ** VERBOSE**
    这个主要是为了方便阅读。
    不用此参数时的写法:
pat = re.compile(r"\s*(?P<header>[^:]+)\s*:(?P<value>.*?)\s*$")

用了VERBOSE参数,则可以写成下面的样子,可以添加注释方便阅读。

pat = re.compile(r"""
 \s*                 # Skip leading whitespace
 (?P<header>[^:]+)   # Header name
 \s* :               # Whitespace, and a colon
 (?P<value>.*?)      # The header's value -- *? used to
                     # lose the following trailing whitespace
 \s*$                # Trailing whitespace to end-of-line
""", re.VERBOSE)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,378评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,356评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,702评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,259评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,263评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,036评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,349评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,979评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,469评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,938评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,059评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,703评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,257评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,262评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,501评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,792评论 2 345

推荐阅读更多精彩内容

  • re模块手册 本模块提供了和Perl里的正则表达式类似的功能,不关是正则表达式本身还是被搜索的字符串,都可以...
    喜欢吃栗子阅读 3,981评论 0 13
  • 说明 摘抄为主,忘记是在什么地方看见的原文了。原作者别打我,若你见了请联系我,增加你的版权和链接描述 整体过程 P...
    明慢慢阅读 1,006评论 0 8
  • 2.和操作系统相关的调用 系统相关的信息模块 import sys sys.argv是一个list,包含所有的命令...
    周倜吉阅读 359评论 0 0
  • 今天是腊八,你们喝腊八粥了吗? 一大早妈妈就给我做了腊八粥,我是喝了满满的一天——三顿的腊八粥,不要羡慕嫉妒恨我有...
    漱玉余清阅读 738评论 0 0
  • 只要保持心的平静 就能从水中看到炊烟的影子 消失的炊烟在空中枯萎 弥漫着无限的相思 我站在青春的高地望去身后的平原...
    泰安左眼皮跳跳阅读 244评论 1 12