正则表达式相对小并且存在限制,所以不是所有的字符串处理任务都能用正则表达式解决。也存在有些任务可以用正则表达式做,但表达式非常复杂。在这些情况下,更好的选择是使用Python代码处理,但Python代码相对正则表达式会更慢,但却可能更好理解。
. ^ $ * + ? { } [ ] \ | ( )首先我们看[和],他们被用于指定一个字符类,表示你希望匹配的一套字符集。字符能被单独列出,或者使用‘-‘来指示字符的范围,例如:[abc]将匹配字符a、b或c的任意一个;[a-c]也是匹配a、b或c中的任意一个。如果你想匹配仅小写字母,那么RE应该为[a-z]。
>>> import re >>> p = re.compile('ab*') >>> p re.compile('ab*')re.compile()也提供了一个可选的flags参数,用于激活各种特征,后面将详细介绍,下面是一个简单的例子:
>>> p = re.compile('ab*', re.IGNORECASE)RE作为一个字符串传给re.compile()。RE被作为字符串处理是因为正则表达式不是Python语言核心的一部分,没有特定的语言用于创建它们。re模块仅仅是Python包含的一个C语言扩展模块,就像socket和zlib模块一样。
>>> import re >>> p = re.compile('[a-z]+') >>> p re.compile('[a-z]+')现在,你能尝试匹配各种字符串,一个空字符串将根本不匹配,由于+意味着‘一个或者更多’,match()将返回None,你能直接打印结果:
>>> p.match("") >>> print(p.match("")) None接下来,我们尝试一个匹配的字符串,这时,match()将返回一个匹配对象,因此你应该存储结果在一个变量中以供后面使用:
>>> m = p.match('tempo') >>> m <_sre.SRE_Match object; span=(0, 5), match='tempo'>现在你能询问匹配对象关于匹配字符串的信息。匹配对象也有几个方法和属性,最重要的几个是:
>>> m.group() 'tempo' >>> m.start(), m.end() (0, 5) >>> m.span() (0, 5)由于match()仅检查RE是否匹配字符串的开始,start()将总是返回0。然而,search()方法扫描整个字符串,因此开始位置不一定为0:
>>> print(p.match('::: message')) None >>> m = p.search('::: message'); print(m) <_sre.SRE_Match object; span=(4, 11), match='message'> >>> m.group() 'message' >>> m.span() (4, 11)在实际编程汇总,通常将匹配对象存入一个变量中,然后检查它是否为None,例如:
p = re.compile( ... ) m = p.match( 'string goes here' ) if m: print('Match found: ', m.group()) else: print('No match')findall()返回匹配字符串的列表:
>>> p = re.compile('\d+') >>> p.findall('12 drummers drumming, 11 pipers piping, 10 lords a-leaping') ['12', '11', '10']findall()在返回结果前必须创建完整的列表,而finditer()则返回匹配对象实例作为一个iterator:
>>> iterator = p.finditer('12 drummers drumming, 11 ... 10 ...') >>> iterator <callable_iterator object at 0x...> >>> for match in iterator: ... print(match.span()) ... (0, 2) (22, 24) (29, 31)
>>> print(re.match(r'From\s+', 'Fromage amk')) None >>> re.match(r'From\s+', 'From amk Thu May 14 19:12:10 1998') <_sre.SRE_Match object; span=(0, 5), match='From '>这些函数创建一个模式对象,并调用它上面的方法,它们也存储编译后的对象到缓存中,以至于未来使用同样的RE将不需要重新编译。
charref = re.compile(r""" &[#] # Start of a numeric entity reference ( 0[0-7]+ # Octal form | [0-9]+ # Decimal form | x[0-9a-fA-F]+ # Hexadecimal form ) ; # Trailing semicolon """, re.VERBOSE)如果没有使用re.VERBOSE,则RE将是这样:
charref = re.compile("&#(0[0-7]+" "|[0-9]+" "|x[0-9a-fA-F]+);")在上面的例子中,Python的自动字符串串联被用于将RE分化到多个片段,但是它任然比使用re.VERBOSE更难理解。
>>> print(re.search('^From', 'From Here to Eternity')) <_sre.SRE_Match object; span=(0, 4), match='From'> >>> print(re.search('^From', 'Reciting From Memory')) None3)$
>>> print(re.search('}$', '{block}')) <_sre.SRE_Match object; span=(6, 7), match='}'> >>> print(re.search('}$', '{block} ')) None >>> print(re.search('}$', '{block}\n')) <_sre.SRE_Match object; span=(6, 7), match='}'>为了匹配字符‘$‘,需要使用\$或者将它分装到字符类中,作为[$]。
>>> p = re.compile(r'\bclass\b') >>> print(p.search('no class at all')) <_sre.SRE_Match object; span=(3, 8), match='class'> >>> print(p.search('the declassified algorithm')) None >>> print(p.search('one subclass is')) None在使用时有两点需要注意:首先,在Python中,\b表示退格字符,ASCII值是8,如果你不用原始字符串,那么Python将转换\b到一个退格字符,你的RE将不按你的设想匹配。下面的例子和我们上面的例子相似,仅有的区别是RE字符串少了‘r‘前缀:
>>> p = re.compile('\bclass\b') >>> print(p.search('no class at all')) None >>> print(p.search('\b' + 'class' + '\b')) <_sre.SRE_Match object; span=(0, 7), match='\x08class\x08'>第二,在字符类里,\b表示退格字符,和Python中的含义表示一致。
>>> p = re.compile('(ab)*') >>> print(p.match('ababababab').span()) (0, 10)组也能获取它们匹配的字符串的开始和结束点,通过传递一个参数到group()、start()、end()和span()。组的编号从0开始,组0总是存在的,他就是整个RE,因此匹配对象方法将组0作为他们的默认参数。
>>> p = re.compile('(a)b') >>> m = p.match('ab') >>> m.group() 'ab' >>> m.group(0) 'ab'子组从左到右编号,从1开始。组能是嵌套的。为了确定编号,从左向右只算开放括号字符。
>>> p = re.compile('(a(b)c)d') >>> m = p.match('abcd') >>> m.group(0) 'abcd' >>> m.group(1) 'abc' >>> m.group(2) 'b'group()一次能被传递多个组编号,这种情况下它将返回一个元组:
>>> m.group(2,1,2) ('b', 'abc', 'b')groups()方法返回包含所有子组匹配的字符串的元组,子组从1开始:
>>> m.groups() ('abc', 'b')在模式中的反向应用允许你指定一个先前组的内容,例如,\1表示在当前位置的内容和组1匹配的内容相同。注意在Python中必须使用原始字符串表示。
>>> p = re.compile(r'(\b\w+)\s+\1') >>> p.search('Paris in the the spring').group() 'the the'这种匹配方式在搜索中很少使用,但在字符串替换时却非常有用。
>>> m = re.match("([abc])+", "abc") >>> m.groups() ('c',) >>> m = re.match("(?:[abc])+", "abc") >>> m.groups() ()除了你不能获取组匹配的内容,一个非捕获组的行为和捕获组的行为完全一致,你能放任何内容在它里面,可以使用重复元字符(例如*)重复它,或者嵌套其它组(捕获或者非捕获)。当修改一个已经存在的模式时(?:...)是特别有用的,因为你可以增加新的组而不改变已有的组的编号。但需要注意,使用非捕获组和捕获组在匹配上没有任何效率上的不同。
>>> p = re.compile(r'(?P<word>\b\w+\b)') >>> m = p.search( '(((( Lots of punctuation )))' ) >>> m.group('word') 'Lots' >>> m.group(1) 'Lots'命名组是便利的,因为名称比编号更容易记忆,下面是一个来自imaplib模块的RE的例子:
InternalDate = re.compile(r'INTERNALDATE "' r'(?P<day>[ 123][0-9])-(?P<mon>[A-Z][a-z][a-z])-' r'(?P<year>[0-9][0-9][0-9][0-9])' r' (?P<hour>[0-9][0-9]):(?P<min>[0-9][0-9]):(?P<sec>[0-9][0-9])' r' (?P<zonen>[-+])(?P<zoneh>[0-9][0-9])(?P<zonem>[0-9][0-9])' r'"')显然使用名称的方式m.group(‘zonem‘)比使用组编号9获取匹配值的方式更加容易使用。
>>> p = re.compile(r'(?P<word>\b\w+)\s+(?P=word)') >>> p.search('Paris in the the spring').group() 'the the'
>>> 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().']有时你不仅对分隔符之间是什么感兴趣,而且需要知道哦分隔符是什么。如果在RE中使用了括号,那么他们的值也将出现在返回列表中。比较下面的调用:
>>> 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', '.', '']模块级的函数re.split()增加了RE作为第一个参数,其余的相同:
>>> re.split('[\W]+', 'Words, words, words.') ['Words', 'words', 'words', ''] >>> re.split('([\W]+)', 'Words, words, words.') ['Words', ', ', 'words', ', ', 'words', '.', ''] >>> re.split('[\W]+', 'Words, words, words.', 1) ['Words', 'words, words.']
>>> 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'subn()方法做同样的事,但是返回一个长度为2的元组,包含新字符串和替换的次数:
>>> 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-'如果replacement是一个字符串,在它里面的任何反斜杠转义符都会被处理。即,\n会被转换为一个新行字符,\r被转换为回车符,等等。未知的转义符例如\j被遗留。反向引用,例如\6,被RE中的对应组匹配的子字符串取代。这让你在替换后的结果字符串中能合并原始字符串的部分。
>>> p = re.compile('section{ ( [^}]* ) }', re.VERBOSE) >>> p.sub(r'subsection{\1}','section{First} section{second}') 'subsection{First} subsection{second}'也可以使用(?P<name>...)命名的组。\g<name>将通过组名来匹配,\g<number>将通过组编号来匹配。因此\g<2>等价于\2,当可以避免歧义,例如\g<2>0表示匹配组2,而\20则会被解释为匹配组20。下面替换的例子都是等价的,但是使用了3种不同的方式:
>>> 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}'replacement也可以是一个函数,可以给你更多的控制。如果replacement是一个函数,函数会处理每一个模式匹配的非重叠的子字符串。在每次调用,函数被传递一个匹配对象作为参数,函数可以使用这个信息计算替换字符串并返回它。
>>> 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.'当使用模块级别的re.sub()函数时,模式作为第一个参数传入。模式可以为一个对象或者字符串;如果你需要指定正则表达式标志,你必须使用一个模式对象作为第一个参数,或者在模式字符串中用嵌入的修饰语,例如:sub("(?i)b+", "x", "bbbb BBBB")返回‘x x‘。
>>> print(re.match('super', 'superstition').span()) (0, 5) >>> print(re.match('super', 'insuperable')) None另一个方面,search()将扫描整个字符串,报告发现的第一个成功匹配。
>>> print(re.search('super', 'superstition').span()) (0, 5) >>> print(re.search('super', 'insuperable').span()) (2, 7)有时你会被引诱使用re.match(),仅仅增加.*到你的RE之前。你应该拒绝这个诱惑,转而使用re.search()。正则表达式编译器会做一些RE的分析,为了加速查找匹配的处理。一个如此的分析是分析出匹配的首字符必定是什么;例如,一个以Crow开始的模式必须匹配首字符‘C‘。这个分析使引擎快速扫描字符串查询开始字符,当‘C‘被发现时才继续向下匹配。
>>> s = '<html><head><title>Title</title>' >>> len(s) 32 >>> print(re.match('<.*>', s).span()) (0, 32) >>> print(re.match('<.*>', s).group()) <html><head><title>Title</title>RE在<html>中匹配‘<‘,然后.*消费字符串其余的所有部分,由于RE最后的>不能匹配,于是正则表达式引擎不得不回溯字符直到它为>找到一个匹配。最后的匹配就是从<html>的‘<‘到</title>的‘>‘,并不是你想要的。
>>> print(re.match('<.*?>', s).group()) <html>(注意使用正则表达式解析HTML或者XML是痛苦的。因为写一个能处理所有场景的正则表达式是非常复杂的,使用HTML或者XML解析器来完成这样的任务。)
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)和下面的表达式比起来,这是更可读的:
pat = re.compile(r"\s*(?P<header>[^:]+)\s*:(?P<value>.*?)\s*$")
原文地址:http://blog.csdn.net/tomato__/article/details/46416183