python学习笔记18-正则表达式

来源:互联网 发布:java实现两个数交换 编辑:程序博客网 时间:2024/06/05 20:17

1. 正则表达式简介

正则表达式(regular expression),是使用单个字符串来描述、匹配一系列符合某个语法的字符串。在python中对应的是re模块。待匹配模式和字符串可以采用unicode编码或者普通8-位编码,但是两者不能混用,即如果待匹配模式是Unicode编码,则字符串也必须是Unicode编码。

我们知道,正则表达式采用反斜杠“\”来表示一些特殊字符(在正则表达式中有特殊意义的字符),这会造成一些困扰。比如,我们需要在正则表达式中匹配反斜杠“\”,那么我们的正则表达式中则需要表示为“\\”,则在编程语言中我们则需要“\\\\”(每两个转义成正则表达式中的一个"\")。而在python中,用raw string表示则不再将"\"看成是特殊字符,比如上面在编程语言中可以写成r"\\",再比如r"\n"则表示为"\"和"n"两个字符。

python的官方文档中有一章节专门讲解正则表达式的,下面的学习就是基于官方文档,也算是边翻译边学习吧。


2. 正则表达式符号

正则表达式包括普通字符和特殊字符,普通字符就是正常的'A','z'之类的,比如RE(正则表达式简写)last会匹配字符串'last'(后面RE都不加引号,字符串都加引号)。而特殊字符则是像‘|’,‘(’这种字符。另外,RE中是不允许出现空字节的,但是可以用\number这种来表示,比如'\x00'。特殊字符如下所示:

‘.’ :(点),默认情况下,它可以匹配除了newline外的任何字符。当DOTALL标识被指定时,它可以匹配任何字符(DOTALL下面会提到)。

'^' :匹配字符串开头。当MULTILINE模式被指定时,可以匹配多行的开头。

‘$’ :匹配字符串末尾,或者在字符串末尾的换行符之前;在MULTILINE模式时,匹配每一行的末尾。

‘*’ :对RE中之前的内容重复0到n次,如ab*,可以匹配'a','ab','abbbb'等。

‘+’ :对RE中之前的内容重复1到n次,如ab+,可以匹配'ab','abbbb',但是不能匹配'a'。

‘?’ :对RE中之前的内容重复0或1次,如ab?,可以匹配'a'或'ab',但'abbbb'则不能匹配。

'*?', '+?', '??' :在学习这几个之前,先介绍下python中的数量词匹配原则,python默认是贪婪(greedy)匹配,就是在满足匹配的情况下尽可能尝试更多的字符;而非贪婪的则相反。比如:用RE <.*>去匹配字符串'<H1>title</H1>',则得到的结果是整个字符串;这有可能不是我们想要的,可以在后面加'?',可以把贪婪匹配改为非贪婪匹配。上例中,用RE <.*?>匹配字符串得到的是<H1>。

'{m}' :匹配之前的字符m次,如a{3},则匹配aaa。

'{m,n}' :匹配之前的字符m到n次,如a{3,5},则可以匹配'aaa','aaaa','aaaaa'。如果省略m,则默认从0开始;省略n,则默认为无穷大。

'{m,n}?' :和之前的'*?'类似,将贪婪匹配改为非贪婪匹配。假设字符串为'aaaaa',用RE a{3,5}匹配该字符串得到'aaaaa',为贪婪的;而用RE a{3,5}?匹配该字符串得到'aaa',为非贪婪的。

‘\’ :转义字符,在正则表达式中可以用\*去匹配特殊字符'*',这种情形下在编程语言中最好用上面谈到的python的raw string r'\*'。

'[]' :可以表示字符的集合。1)可以单独表示,如RE [amk]匹配'a',或'm',或'k' ;2)可以范围表示:如RE [0-5],[a-z]可以分别匹配0到5的字符,a到z的字符;当需要匹配-时,可以用转义字符\,比如[a\-z],则匹配'a','-','z',另外当'-'符号在开头或末尾时,默认认为它是一个匹配字符,比如[a-],则会匹配'a','-' ;3)在[]中,特殊字符会失去其特殊意义,如[(*+]匹配'(','*','+' ;4)字符类,比如\w也可以在集合中,比如\w表示的是字符集[a-zA-z0-9_],另外还有数字集\d [0-9],空白集\s [空格\t\f\r\n\v];以及它们相应的大写\W\D\S表示的取非;5)可以在集合中用^表示匹配除一些字符之外的,如[^a]表示匹配除'a'以外的所有字符,[^^]表示匹配除'^'之外的所有字符(^只有在第一个位置上才有这种取非的意义)6)匹配']',可以用反斜杠来表示匹配']',如[[()\]{}]。

‘|’ :连接两个正则表达式 A|B,表示A或B中任一个被匹配都算匹配成功。另外,当A匹配成功后,不会再去尝试匹配B(换句话说,这个匹配是非贪婪的)。

(...) :表示分组,在括号内的相当于一个组内,若在组内用|,则只在该组内有效;另外,组从左到右编号依次+1。如果需要匹配'(',')',可以用[(],[)]。

(?...) :这个是拓展,?后面的第一个字符决定了含义和下面表达式的构造,这个表达式通常不创建分组,除了(?p<name>...)。

(?aiLmsux) :相当于上一个的实例吧,?后面可以跟'a','i','L','m','s','u','x',只能在正则表达式的开头,每一个字符对应一种匹配标识:re-A(只匹配ASCII字符),re-I(忽略大小写),re-L(locale dependent),re-M(MULTILINE模式), re-S(dot matchs all),re-X(verbose),具体的在下面学习中会涉及到。这种方式比传递参数到re.compile()函数中方便。注意?x符号要用在开头或者一系列空白字符后,如果它的前面有非空白字符,则结果undefined。

(?:...) :(...)的非分组版本,用于使用'|'或后接数量词,这个不太懂。

(?P<name>...) :分组,除了原有的编号,还可以有一个name别名,并且可以在三种方式下被引用:1)在本身的模式中,如(?P<quote>[' '']).*?(?P=quote);2)执行匹配对象m时,如m.group('quote');3)在传递给re.sub()函数的repl参数的字符串中。

(?P=name) :引用一个别名为name的分组,如上例所示。

(?#...) :注释,#后的内容被忽略。

(?=...) :只有当...等于字符串后面的内容才成功匹配,但是不消耗字符串内容。

(?!...) :只有当...不等于字符串后面的内容时才成功匹配,同样不消耗字符串内容。

(?<=...) :只有当前位置的前面与...匹配时,才算匹配成功,返回当前位置后面的字符串内容。也叫做positive lookbehind assertion。

(?<!...) :上面的逆命题,只有当前位置的前面与...不匹配时,才算匹配成功,返回当前位置后面的字符串内容。也叫做negative lookbehind assertion。

(?(id\name)yes-pattern|no-pattern) :如果给出的id或name存在,则会尝试匹配yes-pattern,否则将尝试匹配no-pattern。

下面是一些特殊的序列,包括一个反斜杠\和一个字符,如果下面的列表中不存在这个序列,则看成转义字符(\$,看成$):

\number :匹配相应编号分组的内容?(好像不是...)

\A :仅匹配开头。

\b :仅匹配开头或者结尾,或者在\w和\W之间的内容。如RE r'\bfoo\b' 匹配'foo','foo.','(foo)', 'bar foo bar' (空白字符?),但是不能匹配 'foobb'。

\B :相当于\b的取非,上述的例子反过来就是这个的例子。

\d :匹配Unicode字符集中的所有decimal数字,和ASCII码中的[0-9],通常都认为是匹配[0-9]。

\D :\d的取非,不匹配数字。

\s :匹配Unicode字符集中的所有空白字符,和ASCII中的[ \t\n\r\f\v],通常认为就是[ \t\n\r\f\v]。

\S :\s的取非,不匹配空白字符。

\w :通常认为[a-zA-Z0-9_]。

\W :\w的取非,不匹配\w中的字符。

\Z :仅匹配字符串末尾。

\u 和 \U :通常仅在Unicode字符集下才会出现。

另外,还有如果\后第一个数字是0,且有三位数字,则认为是八进制数。

上述几乎就是python中用到的所有符号,需要经常使用才能加深记忆。


3. 模块内容

这个模块定义一些函数、常量、还有一个异常。下面对这些进行介绍。

1)re.compile(pattern, flags=0) :将一个正则表达式模式编译进一个正则表达式对象中,该对象可以使用match()函数或search()函数来进行字符串匹配。表达式的操作可以通过修改flags的值来进行相应变化,这些值还可以用or操作'|',在下面会讲到。

prog = re.compile(pattern)result = prog.match(string)#等价于result = re.match(pattern, string)
通常情况下,用re.compile()函数进行编译后再使用会比较高效,如果在后面还要多次用到这个模式。

2)re.A,re.ASCII :使\w,\W,\s,\S,\d,\D,\b,\B只进行ASCII匹配,而不是unicode匹配,这个flag只在Unicode模式下才有效,在ASCII模式下自动忽略。

3)re.DEBUG :显示编译pattern时的debug信息。

4)re.I, re.IGNORECASE : 忽略大小写,比如[a-z]也会匹配大写字符,并且这个不受locale的影响,同时也适用于unicode字符集。

5)re.L, re.LOCALE :使得\w,\W.\s,\S,\b,\B的执行取决于当前locale信息,当当前locale不可靠时,这个模式不提倡使用。

6)re.M, re.MULTILINE :当处于这种模式时,'^' 不仅在字符串开头匹配,并且在每一行的开头匹配;'$' 不仅在字符串末尾匹配,并且在每一行的末尾匹配。

7)re.S, re.DOTALL :这种情况下,'.'符号可以匹配任意字符,包括换行字符。

8)re.X, re.VERBOSE :这种模式可以使得正则表达式看起来更好更详细,其中的空白字符会被忽略,除非它在一个set中或它前面有转义字符;另外#后的注释也会被忽略。如下式的a在功能上就等价于b:

a = re.compile(r"""\d +  # the integral part                   \.    # the decimal point                   \d *  # some fractional digits""", re.X)b = re.compile(r"\d+\.\d*")

9)re.search(pattern, string, flags=0) :在string中scan,找到pattern匹配成功的第一个位置,并返回一个相应的match对象;如果没有匹配则返回None,文档中说注意这和在string中查找一个长度为0的match不同。

10)re.match(pattern, string, flags=0) :如果在string首部有0或多个字符匹配成功,返回相应的match对象;否则返回None,与长度为0的匹配不同。注意如果在MULTILINE模式下,re.match()返回的还是string的首部成功匹配,而不是每一行的匹配。(如果想要匹配每一行的,用search())

11)re.fullmatch(pattern, string, flags=0) :如果整个string成功匹配pattern,则返回相应的match对象;否则返回None。

12)re.split(pattern, string, maxsplit=0, flags=0) :根据pattern对string进行分割;如果pattern是在分组内,那么所有分割的元素也会被存储;另外,如果指定maxsplit,那么多于maxsplit的都会被放在一个元素中;另外,如果分割模式匹配不成功,则会直接返回原字符串,而不会进行任何操作。re.split的小例子:

# re.splitprint (re.split('\W+','Words, words, words.'))   # ['Words', 'words', 'words', '']print (re.split('(\W+)','Words,words,words.'))   # ['Words', ',', 'words', ',', 'words', '.', '']print (re.split('\W+','Words,words,words.',1))   # ['Words', 'words,words.']print (re.split('(\W+)','...Words,words,words...')) # ['', '...', 'Words', ',', 'words', ',', 'words', '...', '']print (re.split('[a-f]','0a3B9',flags=re.I))  # ['0', '3', '9']print (re.split('x','foobar'))    # ['foobar']

13)re.findall(pattern, string, flags=0) :返回字符串中所有不重叠的匹配结果,以list形式。如果有多个分组,则返回一个多组的List。

14)re.finditer(pattern, string, flags=0) :返回一个match对象的iterator,来指向所有非重叠的匹配结果。

15)re.sub(pattern, repl, string, count=0, flags=0) :在string中进行查找匹配的位置,并用repl替换,repl可以是字符串,也可以是函数。count表示要替换的次数,如果缺省或为0,则全部替换。小例子:

def dashrepl(matchobj):    if matchobj.group(0) == '-': return ' '    else : return '-'print (re.sub('-{1,2}',dashrepl,'pro----gram-files'))  # pro--gram files


16)re.subn(pattern, repl, string, count=0, flags=0):和上一个函数一样,不过返回的是一个tuple(new_string, number_of_sub_made)。

17)re.escape(string) :除了ASCII字符、数字、_ 外其余的全部丢弃。

18)re.purge() :清空正则表达式缓存。

19)exception re.error :当正则表达式不正确时,会抛出一个re.error异常。


4. 正则表达式对象

已编译的正则表达式对象支持下列函数和属性:(与上面的模块内容相似,但是是已编译好的正则表达式对象调用的)

1)regex.search(string[,pos[,endpos]]) :在string中scan,找到pattern匹配成功的第一个位置,并返回一个相应的match对象;如果没有匹配则返回None,文档中说注意这和在string中查找一个长度为0的match不同。另外,pos指定search在string中的起始位置,默认是0;这和"^"的起始位置有区别。而endpos指定search的搜索长度。如rx.search(string, 0, 50)则表示从位置0开始搜索50位,即到下标为49的位置,该式等价于rx.search(string[:50},0)。

2)regex.match(string,[,pos,[,endpos]]):与第3小节中的re.match()作用相同,且pos,endpos的含义与regex.search()相同。

3)regex.fullmatch(string[,pos,[endpos]]):完全匹配,与第3小节中的11)作用相同,且pos,endpos的含义与regex.search()相同;

4)regex.split(string,maxsplit=0):与第3小节中的12)作用相同;

5)regex.findall(string[,pos[,endpos]]):与第3小节中的13)作用相同;

6)regex.finditer(string[,pos[,endpos]]):与第3小节中的14)作用相同;

7)regex.sub(repl,string,count=0)、regex.subn(repl,string,count=0):与第3小节的15)和16)作用相同;

8)regex.flags:正则表达式对象的匹配标识,这和re.compile()可以结合在一起使用,指定该正则表达式对象的标识;

9)regex.groups:匹配模式中的分组个数;

10)regex.groupindex:dictionary对象,任何以(?P<name>)显示指定的分组都会以分组名和分组编号在该字典中存储;

11)regex.pattern:该正则表达式的pattern字符串。


5. 匹配对象(Match Objects)

Match对象默认都是True,如果使用search()或match()匹配不成功,则会返回None,所以可以使用

match = re.search(pattern,string)

if match:

     process(match)

这种形式来判断和执行match对象。

Match对象包含以下函数和属性:

1)match.expand(template):将匹配到的分组代入template中然后返回。template中可以使用\id或\g<id>、\g<name>引用分组,但不能使用编号0。\id与\g<id>是等价的;但\10将被认为是第10个分组,如果你想表达\1之后是字符'0',只能使用\g<1>0。

2)match.group([group1,... ]):获得匹配后的分组字符串,参数为编号或者别名;group(0)代表整个字符串,group(1)代表第一个分组匹配到的字符串,依次类推;如果编号大于pattern中的分组数或者小于0,则返回IndexError。另外,如果匹配不成功的话,返回None;如果在多行模式下有多个匹配的话,返回最后一个成功的匹配。

3)match.groups(default=None):返回一个tuple,包含所有的分组匹配结果;如果default设为None的话,如果有分组没有匹配成功,则返回"None";若设为0,则返回"0"。

4)match.groupdict(default=None):和上一个相似,不过返回的是dictionary,包含所有命名的分组和其匹配的值,如果有分组没有匹配成功,返回默认值"None"。

5)match.start([group]),match.end([group]):返回字符串中,匹配分组[group]成功的起始和结束下标+1,[group]默认为0;如果[group]存在但是没有匹配成功,返回-1。

6)match.span([group]),返回一个分组[group]成功匹配时的信息,2-tuple,(m.start([group]), m.end([group]));如果分组没有成功匹配,返回(-1,-1)。

7)match.pos:string中开始匹配的下标。

8)match.endpos:string中结束匹配的下标。

9)match.lastindex:返回分组匹配最后成功的分组编号;如果没有一个分组匹配成功,返回None。

10)match.lastgroup:返回分组匹配最后成功的分组别名;如果没有一个分组匹配成功,或者最后一个成功匹配的分组没有别名,返回None。

11)match.re:执行该match对象的正则表达式对象。

12)match.string:传递到match()或search()函数中的字符串。

举个小例子:

import rem = re.match(r"(\w+) (\w+)(?P<groupName>.*)","python fun!")print ("m.group(1,2): ", m.group(1,2))    # m.group(1,2):  ('python', 'fun')print ("m.groups(): ",m.groups())         # m.groups():  ('python', 'fun', '!')print ("m.groupdict(): ",m.groupdict())   # m.groupdict():  {'groupName': '!'}print ("m.start(1): ",m.start(1))         # m.start(1):  0print ("m.end(1): ",m.end(1))             # m.end(1):  6print ("m.span(2): ",m.span(2))           # m.span(2):  (7, 10)<pre name="code" class="python">print ("m.expand(2 1 3)", m.expand(r"\2 \1\3"))  # m.expand(2 1 3) fun python!

上面就是python3.4.2文档关于正则表达式的大体介绍,至于要掌握还是得多练练呀,越来越觉得时间不够用了,keep on learning!










0 0