1. 反斜杠的困扰(The Backslash)
有时候需要匹配的文本带有‘\‘,如‘\python‘,因为正则表达式有些特殊字符有特殊意义,所以需要前面加上‘\‘来消除特殊意义,这里匹配的正则表达式是‘\\python‘,这时候如果要编译这个正则表达式需要re.compile(‘\\\\python‘),因为在传递字符串的时候python本身就需要用‘\\‘来表示‘\‘,也就造成了反斜杠的泛滥。
使用前缀‘r‘可以解决这个问题:’r‘之后的‘\‘只是这个字符本身而没有特殊意义,比如r‘\n‘表示两个符号‘\‘和‘n‘,而原来‘\n‘表示换行符,所以可以写成re.compile(‘\\python‘)
2. match的几种methods
match(): 判断RE是否从第一个字符开始匹配
search(): 判断是否从任意一个位置匹配(不管是否从第一个字符开始)
findall(): 找到RE匹配的所有子字符串,并以list的形式返回
finditer(): 找到RE匹配的所有子字符串,并以iterator的形式返回
1 >>> p = compile(‘[a-z]+‘)
2 >>> print p.match("")
3 None
4
5 >>> m = p.match(‘tempo‘)
6 >>> m.group()
7 ‘tempo‘
8 >>> m.start(), m.end()
9 (0, 5)
10 >>> m.span()
11 (0, 5)
12
13 >>> m = p.search(‘::: message‘)
14 >>> m.group()
15 ‘message‘
16
17 >>> p = re.compile(‘\d+‘)
18 >>> it = p.finditer(‘12 drummers drumming, 11 pipers piping, 10 lords a-leaping‘)
19 >>> for match in it:
20 ... print match.group()
21 ...
22 (0, 2)
23 (22, 24)
24 (29, 31)
3.
‘|‘: “或”,A|B表示匹配A或者B
‘^‘: 匹配行的开头,一般情况下只会匹配字符串的开头,如果在MULTILINE模式下则可以匹配每行的开头
1 >>> print re.search(‘^From‘, ‘From Here to Eternity‘)
2 <_sre.SRE_Match object at 0x...>
3 >>> print re.search(‘^From‘, ‘Reciting From Memory‘)
4 None
‘\A‘: 只匹配字符串的开头
‘$‘: 匹配行的末尾,一般情况下只会匹配字符串的末尾,如果在MULTILINE模式下则可以匹配每个换行符
‘Z‘: 只匹配字符串的末尾
‘\b‘: 单词的边界,即单词的开头或者末尾,可以是空格或者非字母数字的字符
1 >>> p = re.compile(r‘\bclass\b‘)
2 >>> print p.search(‘no class at all‘)
3 <_sre.SRE_Match object at 0x...>
4 >>> print p.search(‘the declassified algorithm‘)
5 None
6 >>> print p.search(‘one subclass is‘)
7 None
注意上面用来编译的正则前面要带‘r‘,因为在Python中‘\b‘是退格键,如下:
1 >>> p = re.compile(‘\bclass\b‘)
2 >>> print p.search(‘no class at all‘)
3 None
4 >>> print p.search(‘\b‘ + ‘class‘ + ‘\b‘)
5 <_sre.SRE_Match object at 0x...>
‘\B‘: 匹配不是单词边界
4. Grouping
用‘()‘括起来表示分组,比如(ab)*可以匹配ab0次或者更多次。
分组是有编号的,0表示整个RE,从左往右编号依次加一,只要看左括号的位置就行
1 >>> p = re.compile(‘(a(b)c)d‘)
2 >>> m = p.match(‘abcd‘)
3 >>> m.group(0)
4 ‘abcd‘
5 >>> m.group(1)
6 ‘abc‘
7 >>> m.group(2)
8 ‘b‘
5. Non-capturing Groups
()里面跟着?:的是non-capturing group,一般用于一些分组后面不需要用到的时候
1 >>> m = re.match("([abc])+", "abc")
2 >>> m.groups()
3 (‘c‘,)
4 >>> m = re.match("(?:[abc])+", "abc")
5 >>> m.groups()
6 ()
原来编号1的分组在加上?:之后被忽略了,可以用在筛选一些没有用的分组。比较令人疑惑的是第2行m.groups()为什么是‘c‘,这里我觉得应该是贪婪模式尽量往后找的原因
6. Named Groups
分组过多的时候编号难以记住,可以给分组加上一个名字,相当于一个“标签”,后面引用的时候可以直接通过名字来引用,用法是(?P<name>...)
1 >>> p = re.compile(r‘((?P<word>\b\w+\b))‘)
2 >>> m = p.search( ‘(((( Lots of punctuation )))‘ )
3 >>> m.group(‘word‘)
4 ‘Lots‘
5 >>> m.group(1)
6 ‘Lots‘
还可以用来搜索与前面重复的字段:
1 >>> p = re.compile(r‘(?P<word>\b\w+)\s+(?P=word)‘)
2 >>> p.search(‘Paris in the the spring‘).group()
3 ‘the the‘
7. Lookahead Assertions
(?=...) 需要在这个位置匹配括号里面之后的内容,匹配成功即可,不会消耗字符串的内容。
(?!...) 与前一个相反,不能匹配括号里面之后的内容才能匹配成功,不会消耗字符串的内容。
如在匹配带有扩展名的文件的时候,如果想要排除掉带有后缀.bat和.exe的文件,可以用 .*[.](?!bat$|exe$).*$
8. Splitting Strings
1 >>> p = re.compile(r‘\W+‘)
2 >>> p2 = re.compile(r‘(\W+)‘)
3 >>> p.split(‘This... is a test.‘)
4 [‘This‘, ‘is‘, ‘a‘, ‘test‘, ‘‘]
5 >>> p2.split(‘This... is a test.‘)
6 [‘This‘, ‘... ‘, ‘is‘, ‘ ‘, ‘a‘, ‘ ‘, ‘test‘, ‘.‘, ‘‘]
如果使用括号将匹配内容括起来,那么在分割的时候匹配内容也会作为分割的一部分,否则匹配内容会被忽略。如上面通过空格将单词分开,加了括号之后空格也会记录为结果。
.split()函数可以加进参数maxsplit表示最多分割几段,如:
1 >>> p = re.compile(r‘\W+‘)
2 >>> p.split(‘This is a test, short and sweet, of split().‘)
3 [‘This‘, ‘is‘, ‘a‘, ‘test‘, ‘short‘, ‘and‘, ‘sweet‘, ‘of‘, ‘split‘, ‘‘]
4 >>> p.split(‘This is a test, short and sweet, of split().‘, 3)
5 [‘This‘, ‘is‘, ‘a‘, ‘test, short and sweet, of split().‘]
9. Search and Replace
p = re.compile(‘...‘) , p.sub(replacement, string, [count = 0])
string是原来用来被查找替换的字符串,replacement是查找到之后替换的内容,count是最多替换的个数,默认是0表示全部替换
1 >>> p = re.compile( ‘(blue|white|red)‘)
2 >>> p.sub( ‘colour‘, ‘blue socks and red shoes‘)
3 ‘colour socks and colour shoes‘
4 >>> p.sub( ‘colour‘, ‘blue socks and red shoes‘, count=1)
5 ‘colour socks and red shoes‘
subn()使用方法类似,但是会同时返回找到替换的个数
1 >>> p = re.compile( ‘(blue|white|red)‘)
2 >>> p.subn( ‘colour‘, ‘blue socks and red shoes‘)
3 (‘colour socks and colour shoes‘, 2)
4 >>> p.subn( ‘colour‘, ‘no colours at all‘)
5 (‘no colours at all‘, 0)
之前有命名分组的使用方法,(?P<name>...),后面想替换成这个分组的内容可以用\g<name>来替换,或者替换成第2组的内容可以用\2,但是替换成第2组内容后面再加上一个0就会写成\20,会与第20组产生误会,所以有另一个写法是\g<2>,下面例子用了这3种方法,将所有的section替换成subsection
1 >>> p = re.compile(‘section{ (?P<name> [^}]* ) }‘, re.VERBOSE)
2 >>> p.sub(r‘subsection{\1}‘,‘section{First}‘)
3 ‘subsection{First}‘
4 >>> p.sub(r‘subsection{\g<1>}‘,‘section{First}‘)
5 ‘subsection{First}‘
6 >>> p.sub(r‘subsection{\g<name>}‘,‘section{First}‘)
7 ‘subsection{First}‘
还有一种更复杂的方法是用一个函数进行替换,每次会对match到的内容进行一定的操作,如下面将数字转化为16进制的时候,首先将match到的内容作为参数传进函数进行处理再返回16进制数
1 >>> def hexrepl(match):
2 ..."Return the hex string for a decimal number"
3 ...value = int(match.group())
4 ...return hex(value)
5 ...
6 >>> p = re.compile(r‘\d+‘)
7 >>> p.sub(hexrepl, ‘Call 65490 for printing, 49152 for user code.‘)
8 ‘Call 0xffd2 for printing, 0xc000 for user code.‘