说明
- 是基于官方的how to文档来学习的,并不是全面的说明,也不是翻译。更像是随笔记录。
https://docs.python.org/2/howto/regex.html - 正则表达式的基本语法在维基页面有完整的
https://zh.wikipedia.org/wiki/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F
基本的用法
- 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',)
从这个结果可以看到:
- 匹配的原意是a和b中间夹一个数字,并且多次匹配,所以最终的匹配结果是整个字符串
- group()的结果和group(0)的结果是一样的
- 由于只有一对(),所以group的下标只到1,其输出结果就是捕获的一对();不过让人意外的是,输出的是最后一对,而不是第一对。
- 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对()变成了4对。其中左边的是3对嵌套的(),右边是单独的一对()
- 从输出的结果看,先左后右,先外后里的进行匹配输出
-
例子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,就是常规的匹配。主要是查看常规匹配下的group的值
- 代码2是加上了\0,也就是group(0)。但是似乎不允许这样,group(0)实质就是group()本身。
这样做似乎产生了一个递归,所以匹配结果就是None。第一次匹配结果是the..,加上\0后,\0本身的结果就变成了the..the..,然后再加一次本身。这样也能去解释匹配为0.
仔细去观察4段代码的运行结果,gourp(1)和group(2)的结果一直都是一样的。 - 代码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
上面的运行结果,可以得出如下的内容:
- (?=...)的匹配结果也是不会记录在group中的
- 例子2的结果,说明其实是匹配上了的,但是奇怪的是group()的结果却没有匹配值
- 例子3就在要匹配的字符串前加上数字1,发现还是没有输出结果
- 最后修改了表达式,在之前增加了\d以匹配例子3增加的数字1,结果发现是有输出的,输出结果就是\d匹配的1。
- 看到例子4之后,就在考虑如果后面增加匹配呢?
首先例子5在+后面增加了一个\d
的匹配,并在比对的字符串中再最后增加了2
。例子5用:
替换了=
,从输出结果看,匹配是成功的。 - 例子6将例子5的
:
再替换回=
,我们发现匹配是失败的,返回的对象是None
。 - 在例子7中,我们将匹配的表达式中在例子5增加的哪个
/d
再取消,匹配成功,返回值跟例子4一样。 - 例子把是在例子4基础上做修改,将比对的字符串的1去掉。从结果看,可以确定是先匹配整个表达式,而不仅仅是先匹配
...
综合上面的实例,我们可以对(?=...)
得出如下总结:
** 1. 此匹配表达式只能放在最后,针对()单元的+等重复标识除外**
** 2. 首先是匹配整个表达式,而不仅仅是...
。在例子8中,就是先匹配r"\d(?=[abc])+"。 ** 3. 当整个表达式匹配成功后,其会找到
...的前一个表达式,再从
...匹配的字符往前看其前一个表达式的匹配的值,并作为最后输出。如果没有,则为空。在我们的例子中前一个表达式是
/d,匹配的值是
1`。**
Modifying Strings
- ** split()**
- Split的参数是分割的标识,还可以加上最多分成几组的参数。
- 分割的结果是,除开被匹配上的字符,其余的字符组成结果
- 结果的最后一个,固定是空字符串
- 比较特殊的是,分割匹配包含在
()
中的话,输出的结果也包含匹配的字符串。
直接引用官方文档的例子,就完全可以说明了
>>> 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)