余弦:我没看过python的书,但我通读过python文档。文档不过一遍,视野会局限。 近日读sqlmap源码,发现自己对于标准库所知甚少,想到knownsec技能表中余弦的一番话,加之最近有意提升English能力,遂意通读python2.7.13官方文档,去粗取精,译成中文,国内虽有一译站点已有翻译,但为参考。然本钱平平,词不达意,技止于此,望看客海涵。

Python标准库-re——正则表达式操作

本模块提供正则表达式匹配操作,这和Perl的正则很像。要匹配的模式和字符串既可以是Unicode串,也可以是8位字符串。

正则表达式使用反斜杠字符’'来表示特殊的格式以消除特殊字符的效果,成为普通字符。这与字符串字面值中相同目的的同字符背道而驰;例如,匹配一个字面值的反斜杠,模式字符串必须写成'\\\\',因为正则表达式必须是\\,每个反斜杠都要写成'\\'

解决的方案是使用python的原始字符串来表示模式字符串;以’r’做为前缀的字符串不会对反斜杠进行转义。因此r’\n’是一个两个字符的字符串,包含’'和’n’,同时”\n”是只包含一各字符——换行符的字符串。通常python代码中模式字符串使用原始字符串书写。

注意到大多数正则表达式操作可以利用模块级别的方法和RegexObject的方法。这些函数是快捷的形式,无需编译一个正则表达式对象,但会缺少一些调优的参数。

正则表达式语法

正则表达式(RE)制定了一个匹配的字符串集合;本模块的函数可以让你检查一个特定的字符串是否匹配给定的正则表达式(或给定的正则表达式是否匹配特定的字符串,这是一回事)。

正则表达式可以通过连结形成新的正则表达式。如果A和B是正则,那么AB也是正则。通常来说,如果字符串p匹配A,字符串q匹配B,那么pq就匹配AB。除非A或B包含低优先级的操作、A和B之间存在边界条件或存在编号组引用。因此,复杂的表达式可以轻易的由简单表达式组成。关于正则的理论和实现,可以咨询Friedl book,或者任意的有关编译器构造的教科书。

下面是对正则表达式的简单解释。更多信息和优雅的演示,请参考Regular Expression HOWTO。

正则表达式可以包含特殊和普通字符。大多数普通字符,如’A’,’a’或’0’,是最为简单的正则;它们仅仅匹配自己。你可以连接它们,因而last匹配字符串’last’。

一些字符,比如'|''('是特殊的。特殊字符 要么表示某个类别的普通字符,要么影响周围的正则如何解释。正则模式字符串不能包含空字节,但可以用\number的形式来指定空字节,例如’\x00’。

重复限定词(*,+,?,{m,n}等)不能直接嵌套。这避免了非贪婪修饰后缀?的二义性,以及一些其他实现中的修饰符。内部重复中想要应用第二个重复,需要用小括号。例如,表达式(?:a{6})*匹配任意个六连a字符。

特殊的字符:

  • '.'
    • 点。默认模式,匹配除了换行符的任意字符。如果给定DOTALL标志,它匹配含换行符的任意字符。
  • '^'
    • 匹配字符串的起始,在MULTILINE模式下也匹配每个行首。
  • '$'
    • 匹配字符串末尾或换行符前的结尾,MULTIMODE模式也匹配换行符前的位置。foo匹配’foo’和’foobar’,然而foo$仅匹配’foo’。在’foo1\nfoo2\n’中搜索foo.$会匹配’foo2’,在MULTILINE模式下则匹配’foo1’;在’foo\n’中搜索单个$会找到两个匹配:一个是换行符前,一个是字符串末尾。
  • '*'
    • 匹配前面重复出现的正则0到多次,尽可能多的匹配。ab*匹配’a’,’ab’或’a’后跟随任意数量的’b’。
  • '+'
    • 匹配前面重复出现的正则1到多次。ab+匹配’a’跟随任意数目的’b’,不匹配’a’。
  • '?'
    • 匹配前面出现的正则0到1次。ab?匹配’a’或’ab’。
  • *?,+?,??
    • '*','+','?'限定符都是贪婪的,它们会尽可能的匹配。有些时候这是不可取的;当<.*>匹配<a>b<c>时,匹配的是整个串,而不是<a>。增加?限定符可以让它表现的非贪婪匹配,也就是最小化的适配;此时仅仅匹配<a>
  • {m}
    • 匹配当前RE重复的次数为m次,少于m的串不会匹配,比如a{6}严格匹配6个’a’,而不是5个。
  • {m, n}
    • 匹配当前RE m到n次,尝试尽可能多的匹配。例如a{3,5}匹配3到5个’a’字符。m缺省为0,n缺省为无限大。例如,a{4,}b匹配’aaaab’或1000个’a’接一个’b’,但’aaab’不行。逗号不可以省略。
  • '\'
    • 对任意特殊字符转义,比如’*‘和’?’,或表示一个特殊的序列,下面会描述特殊序列。
    • 如果你没有使用原始字符串来表示模式字符串,记住python也可以使用反斜杠来从字符串字面值转义序列,如果转义序列并不被python解释器认识,那么反斜杠和子序列字符被包含在结果字符串中。然而,如果python认出了结果序列,反斜杠应该被重复两次。理解起来较为复杂,因此使用原始字符串更为的简单。
  • []
    • 用于指示一个字符集。在集合中:
      • 字符可以各自罗列,比如[amk]匹配’a’,’m’或’k’。
      • 给出两个字符并用’-‘连接,比如[a-z]表示匹配任意小写字母,[0-5][0-9]匹配00到59的所有两位数,[0-9A-Fa-f]匹配十六进制数。如果’-‘带有转义,或者出现在首尾处,则匹配’-‘的字面值。
      • 特殊字符在集合中会丢失原本的特殊意义。例如[(+*)]匹配四个字符’(‘,’+’,’*’,’)’。
      • 字符类比如\w\S也被集合所接受,尽管它们匹配的字符取决于LOCALE或UNICODE模式是否是强制的。
      • 不在一段范围之内的字符可以通过补集匹配。如果集合的第一个字符是’^’,那么所有不在集合中的字符都被匹配。例如[^5]除了5之外都匹配,[^^]匹配除了’^’的所有字符。’^’如果不是集合的第一个字符,则没有特殊意义。
      • 匹配字面的’]’,则需要用反斜杠转义,或者放在放在集合的前面。比如[()[\]{}][]()[{}]都匹配括号。
  • '|'
    • A|B,A和B可以是任意的RE,这一RE意味着可以匹配A或B的一个。'|'可以分隔任意数量的正则。这也可以用于内部组。当目标字符串被扫描时,RE尝试用'|'从左到右分隔。当一个模式字符串完全匹配时,接受这个分支。这就意味着一旦A匹配了,B就不会再做尝试,尽管他可能会匹配的更长。换句话说,'|'操作符是非贪婪的。想要匹配一个字面值的'|',需要用\|转义,或者用符号类闭合它,比如[|]
  • (...)
    • 匹配小括号中的正则表达式,指定了一组的起始与结尾;组的内容可以在匹配之后回溯出来,并且可以用于匹配在其后的含\number特殊序列的字符串中,下面会进行描述。想要匹配字面值'('')',需要用\(\)转义,或者使用字符类闭合,如[(] [)]
  • (?...)
    • 扩展符号('?'跟随'('不是有意义的其他值)。'?'后的第一个字符决定了这个结构的意义以及更多的语法。扩展通常不会创建一个新组;(?P<name>...)是个唯一的例外。下面是当前支持的扩展。
  • (?iLmsux)
    • (一或多个字母,源于集合'i','L','m','s','u','x'。)该组匹配空串;字母设置相应的标志:re.I(忽略大小写),re.L(依赖区域设置),re.M(多行匹配),re.S(点’.’匹配所有字符),re.U(Unicode依赖),re.X(verbose),对于整个RE生效。(标记在Module Contents——模块内容一节有所描述。)如果你想包含一个标志作为正则的一部分,这是十分有用的,如此则不必给re.compile()函数传递一个flag参数。
    • 注意到(?x)标记会改变表达式如何被传递。他应该在表达式字符串的开始使用,或者在一到多个空白字符后使用。如果标记前没有空白字符,则结果是undefined。
  • (?:...)
    • 非捕获版本的分组正则表达式。匹配括号里的内容,但是匹配到的内容不会作为一个分组,在匹配之后也就不能被回溯到。
  • (?P<name>...)
    • (...)很像,但是通过分组匹配到的子串可以通过符号组名称name访问。组名字必须是有效的python标记符,每个组名称都必须被定义一旦使用了这样一个正则表达式。符号组也是数字顺序的组,就好比组没有被命名一样。
    • 命名组可以通过三个上下文引用。如果模式字符串是(?P<quote>['"].*?(?P=quote))(匹配用单引号或双引号括起来的字符串)。
组”quote”的上下文引用 引用方式
用模式字符串本身 (?P=quote)\1
处理匹配对象m时 m.group('quote')m.end('quote')等等
作为字符串传递给re.sub()的参数repl时 \g<quote> \g<1> \1
  • (?P=name)
    • 命名分组的反向引用;匹配前面匹配到的对应命名分组内容。
  • (?#...)
    • 注释;内容仅仅被忽略。
  • (?=...)
    • 匹配字符串如果...匹配了字符串的跟随串,但不保证是任何的串。这调用一个前向断言。例如,Isaac (?=Asimov)会匹配’Isaac’当且仅当它跟随’Asimov’。
  • (?!...)
    • 如果...不匹配字符串的跟随串,则视为匹配。这是一个前向断言的反向结果。例如,Isaac (?!Asimov)当它不跟随’Asimov’时会匹配’Isaac’。
  • (?<=...)
    • 匹配某个位置前面有...的字符串。这被成为积极回顾断言。(?<=abc)def会匹配abcdef,匹配def时反向看3个字符并检查是否包含模式字符串。包含的模式字符串必须只能匹配一些固定长度的串,这意味着abca|b是允许使用的,而a{3,4}则不行。组引用不被支持,尽管他们可以匹配一些定长字符串。注意到从反向断言起始的模式字符串不会匹配被搜索字符串的起始位置;这时你会更希望用search()函数而不是match()函数。
      >>> import re
      >>> m = re.search('(?<=abc)def', 'abcdef')
      >>> m.group(0)
      'def'
      >>> m = re.search('(?<=-)\w+', 'spam-egg')
      >>> m.group(0)
      'egg'
      
  • (?<!...)
    • 如果某个位置前面不是...串,则进行匹配。这被称为消极回顾断言。和积极反向断言很像,包含的模式字符串必须只能匹配定长字符串,不能包含组引用。模式字符串可以匹配搜索字符串的起始位置。
  • (?(id/name)yes-pattern|no-pattern)
    • 尝试使用yes-pattern匹配,如果组中id或name存在。否则使用no-pattern匹配。no-pattern是可选的可以被忽略。例如,(<)?(\w+@\w+(?:\.\w+)+)(?(1)>)是一个email的匹配模式字符串,会匹配<user@host.com>user@host.com字符串,但不会匹配<user@host.com
    • 2.4新增。

包含’'的转义序列和字符在下表列出。如果普通字符没有列出来,则RE会匹配第二个字符。例如,\$匹配字符’$’。

  • \number
    • 匹配相同号码的第几组。组从1开始标号。例如(.+) \1匹配the the55 55,但是thethe不行(注意组后的空白字符)。转义序列仅仅只能匹配前99组中的一个。如果第一个数字是0,或者是3位数,则不会按组匹配来解析,而是按照八进制数来解析。在’[‘和’]’中的字符类,所有数字的转义被视为字符。
  • \A
    • 仅仅匹配字符串的开始
  • \b
    • 匹配空串,但是仅匹配单词的起始或结尾。单词是字母数字下划线定义的序列,所以单词的结尾是空白字符或其他非字母数字下划线的字符。注意到形式上\b定义为\w和\W字符的边界,或是\w和字符串起始结尾的边界。因此精确的字符集合被认为是字母数字的话,要依赖于UNICODE和LOCALE标记。举个例子,r’\bfoo\b’匹配’foo’,’foo.’,’(foo)’,’bar foo baz’但是不匹配’foobar’或’foo3’。在字符范围内,\b代表回退字符,为了兼容python字符串字面值。
  • \B
    • 匹配空串,仅仅不在单词的起始或结尾。这意味着r'py\B'匹配’python’,’py3’,’py2’,但不匹配’py’,’py.’或’py!’。\B与\b相反,因此也取决于LOCALE和UNICODE标志位设置。
  • \d
    • UNICODE标记没指定时,匹配任意十进制数。也就是和[0-9]相同。UNICODE指定时,匹配在Unicode字符库中被归类为十进制数的那些字符。
  • \D
    • 如果UNICODE标记没指定,匹配任意非十进制字符,也就是[^0-9]。UNICODE指定时,匹配在Unicode字符库中被归类为十进制数以外的那些字符。
  • \s
    • 如果没有指定UNICODE标记,匹配任意空白字符,这和[ \t\n\r\f\v]等同。LOCALE标记不会影响空白的匹配。如果UNICODE被设置,会匹配字符[ \t\n\r\f\v]加上Unicode字符库中被认为空白的其他字符。
  • \S
    • 当UNICODE标记没有指定时,匹配非空白字符,和[^ \t\n\r\f\v]等同。LOCATE标记不会影响非空白的匹配。如果UNICODE指定,则Unicode字符库中被认为不是空白的字符也会被匹配。
  • \w
    • 当LOCALE和UNICODE标记没被指定,匹配任意字母数字下划线字符。和[a-zA-Z0-9_]等同。LOCALE被指定时,匹配[0-9_]和被认为字母或数字的其他字符。如果UNICODE被指定,匹配[0-9_]加上其他Unicode字符库中被认为字母或数字的字符。
  • \W
    • 当LOCALE和UNICODE标记没被指定时,匹配任意非字母数字下划线字符。和[^a-zA-Z0-9_]等同。LOCALE被指定时,匹配非[0-9_]和被认为不是字母或数字的其他字符。如果UNICODE被指定,匹配非[0-9_]加上其他Unicode字符库中被认为不是字母或数字的字符。
  • \Z
    • 仅匹配字符串的结尾。

如果LOCALE和UNICODE标记被特别序列所包含,LOCALE标记影响效果优先于UNICODE。

除了python字面值支持以外的大多数标准转义也被正则接受: \a \b \f \n \r \t \v \x \\

\b用于表示字符间隔,只在字符类中代表回退。

八进制转义由一个限定格式所包含:如果第一个数字是0,或者有3个八进制数,则被认为是八进制转义。否则,就是一个组引用。对字符串字面值来说,八进制转义总是3个数字的长度。

模块内容

该模块定义了多个函数,常量和一个异常。其中的一些函数是为已编译正则表达式提供的完整特性函数的简化版本。大部分重要的应用总是使用已编译的格式。

  • re.compile(pattern, flags=0)
    • 编译一个正则表达式模式字符串成一个正则表达式对象,可以用来用match()和search()方法匹配字符串,下面有所描述。
    • 表达式的行为可以通过指定一个flags值来改变。可以是下面描述的任意变量组合,使用'|'进行组合。
    • 序列
prog = re.compile(pattern)
result = prog.match(string)

# equals
result = re.match(pattern, string)
    • 但是使用re.compile()并且保存正则表达式对象的结果,便于以后再次使用是很有用的方式,这样就不必每次都编译。
  • re.DEBUG
    • 显示已编译表达式的debug信息。
  • re.I
  • re.IGNORECASE
    • 匹配时忽略大小写,像[A-Z]也可以在此时匹配小写字母。不会受本地区域设置而影响。
  • re.L
  • re.LOCALE
    • \w \W \b \B \s \S依赖于本地区域设置。
  • re.M
  • re.MULTILINE
    • 当指定时,模式字符^匹配字符串首部以及每一行的首部;$匹配字符串尾部以及每一行的尾部。
  • re.S
  • re.DOTALL
    • .匹配所有字符。默认情形下是不匹配换行符的所有字符。
  • re.U
  • re.UNICODE
    • \w \W \b \B \d \D \s \S依赖于Unicode字符库。
    • 2.0新增。
  • re.X
  • re.VERBOSE
    • 这一标志允许你通过可视化的分隔模式字符串的逻辑区段以加入注释的方式,写看起来较为漂亮,可读性高的正则表达式。空白字符会被忽略,除非在字符类或由一个反斜杠引导。当某行包含一个不在字符类也不是由反斜杠引导的'#'字符时,其后的所有内容直到换行都是注释,匹配时忽略。
    • 这意味着下面的两种写法匹配的结果一致:
      a = re.compile(r"""\d +  # the integral part
                   \.    # the decimal point
                   \d *  # some fractional digits""", re.X)
      b = re.compile(r"\d+\.\d*")
      
  • re.search(pattern, string, flags=0)
    • 扫描字符串,查找第一个正则表达式匹配的位置,返回一个相应的MatchObject实例。返回None如果没有找到。注意到这和某些差找一个长度为0的字符串匹配不是一回事。
  • re.match(pattern, string, flags=0)
    • 如果字符串起始的0到多个字符匹配了正则表达式,返回一个相应的MatchObject实例。如果找不到返回None。注意这和查找长度为0的串不同。
    • 尽管在MULTILINE模式,re.match()也仅仅匹配字符串起始的位置而不是每行的起始。
    • 如果你想定位一个在字符串中任意位置的匹配,使用search()替代。
  • re.split(pattern, string, maxsplit=0, flags=0)
    • 使用pattern出现的位置分割字符串。如果模式字符串中采用了分组,则所有的组也会填充到返回列表里。如果maxsplit非0,则表示至多分割maxsplit次,剩余的串作为最后一个list的元素返回。
      >>> 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.']
      >>> re.split('[a-f]+', '0a3B9', flags=re.IGNORECASE)
      ['0', '3', '9']
      
    • 如果在分隔符中有捕获组,并且它能够匹配字符串的起始位置,则返回list首元素会增加一个空串,对于结尾来说也是一样的。
      >>> re.split('(\W+)', '...words, words...')
      ['', '...', 'words', ', ', 'words', '...', '']
      
  • - 这种情况下,分隔符组件总是会在结果list中以相同的相对索引中发现(比如,如果有一个分隔符分组,则索引位置为0,2,4。。。)。 - 注意到split永远不会用空的模式匹配分割字符串。例如:
    >>> re.split('x*', 'foo')
    ['foo']
    >>> re.split("(?m)^$", "foo\n\nbar\n")
    ['foo\n\nbar\n']
    
    • 2.7新增:增加了可选的flags参数。
  • re.findall(pattern, string, flags=0)
    • 返回字符串中匹配模式字符串的所有非覆盖结果。string从左向右扫描,返回的结果与匹配顺序相符。如果pattern存在一到多个组,返回一个组列表。如果pattern不止有1个组,则返回的是衣蛾元组列表。空匹配也包含在匹配结果中,除非他们恰好可作为另一个匹配的起始。
    • 1.5.2新版本。
    • 2.4更改:增加可选的flags参数。
  • re.finditer(pattern, string, flags=0)
    • 返回一个迭代器用于生成string中匹配pattern的非覆盖所有结果的MatchObject实例。字符串从左向右扫描,返回结果和匹配顺序相同。空匹配会被包含在结果中除非其恰好可作为另一个匹配的起始。
    • 2.2新增。
    • 2.4更改:增加flags可选参数。
  • re.sub(pattern, repl, string, count=0, flags=0)
    • 返回用repl替代那些匹配pattern的非覆盖部分的字符串。如果pattern没有找到,string原封不动返回。repl可以是字符串,也可以是函数。如果是字符串,则任何的反斜杠转义都会在操作中起作用。也就是说,\n表示换行,\r表示回车等等。不认识的转义如\j就保留下来。反向引用,如同\6会用匹配pattern的第六组内容替换。例如:
      >>> re.sub(r'def\s+([a-zA-Z_][a-zA-Z_0-9]*)\s*\(\s*\):',
      ...        r'static PyObject*\npy_\1(void)\n{',
      ...        'def myfunc():')
      'static PyObject*\npy_myfunc(void)\n{'
      
    • 如果repl是函数,则对每个出现的匹配部分都调用一次。该函数只携带单独的匹配对象作为参数,返回替换的字符串。例如:
      >>> def dashrepl(matchobj):
      ...     if matchobj.group(0) == '-': return ' '
      ...     else: return '-'
      >>> re.sub('-{1,2}', dashrepl, 'pro----gram-files')
      'pro--gram files'
      >>> re.sub(r'\sAND\s', ' & ', 'Baked Beans And Spam', flags=re.IGNORECASE)
      'Baked Beans & Spam'
      
    • pattern可以是string也可以是RE对象。
    • 可选参数count是最大匹配的次数,count必须是非负整数。如果忽略或为0,则匹配所有然后替换。空匹配仅当与前一个匹配不毗邻时进行替换,因此sub('x*','-','abc')返回’-a-b-c-‘。
    • repl参数为string类型时,在上面描述的字符转义和反向引用以外,\g<name>会使用命名为name的匹配组来替代,就像定义在(?P<name>...)语法那样。\g<number>则按组的顺序取到。\g<2>就等同于\2,但可能会引起歧义比如\g<2>0\20可能会因字面跟随的字符’0’而被解析成第20组,而不是第2组。反向引用\g<0>替补了RE匹配到的整个子串。
    • 2.7修改:增加可选参数flags。
  • re.subn(pattern, repl, string, count=0, flags=0)
    • 和sub()行为相同,但是返回的是元组(new_string, number_of_subs_made)。
    • 2.7更新:增加flags可选参数。
  • re.escape(pattern)
    • 转义pattern的所有字符,除了ASCII字母和数字。如果你想匹配一个任意字面值字符串且其包含正则表达式元字符时,会非常有用。例如:
>>> print re.escape('python.exe')
python\.exe

>>> legal_chars = string.ascii_lowercase + string.digits + "!#$%&'*+-.^_`|~:"
>>> print '[%s]+' % re.escape(legal_chars)
[abcdefghijklmnopqrstuvwxyz0123456789\!\#\$\%\&\'\*\+\-\.\^\_\`\|\~\:]+

>>> operators = ['+', '-', '*', '/', '**']
>>> print '|'.join(map(re.escape, sorted(operators, reverse=True)))
\/|\-|\+|\*\*|\*
  • re.purge()
    • 清除正则表达式缓存。
  • exception re.error
    • 当字符串传递给一个函数时,且没有一个有效的正则表达式时会抛该异常(例如,可能包含一些不闭合的括号)。也可能在编译或匹配时遇到某些其他错误时抛出。当字符串不包含模式字符串的匹配时,这一异常永远不会抛出。

正则表达式对象

  • class re.RegexObject
    • 该类支持下列方法和属性:
    • search(string[, pos[, endpos]])
      • 扫描字符串找到一个位置直到字符串匹配正则,返回相应的MatchObject实例。如果没找到则返回None。注意到这和找到0字节长度的匹配项不是一回事。
      • 可选的第二个参数pos给了string一个索引,表明从哪里开始匹配,默认为0。这不完全代表切片字符串。^模式字符串匹配实际的字符串起始以及某些情况新行后的起始,但是这里通过索引来匹配起始是行不通的。
      • 可选的endpos参数限制了可以搜索多远;它不会超过字符串的长度,因此仅在pos到endpos-1范围内会进行匹配。如果endpos小于pos,不会匹配任何东西,否则,如果rx是编译的正则表达式对象,rx.search(string,0,50)和rx.search(string[:50],0)相同。
>>> pattern = re.compile("d")
>>> pattern.search("dog")     # Match at index 0
<_sre.SRE_Match object at ...>
>>> pattern.search("dog", 1)  # No match; search doesn't include the "d"
    • match(string[, pos[, endpos]])
      • 在字符串起始位置匹配了一到多个字符,则返回对应的MatchObject实例。如果匹配不到返回None。注意到这和匹配了长度为0的结果不同。
      • 可选的pos参数和endpos参数和search()中的意义相同。
>>> pattern = re.compile("o")
>>> pattern.match("dog")      # No match as "o" is not at the start of "dog".
>>> pattern.match("dog", 1)   # Match as "o" is the 2nd character of "dog".
<_sre.SRE_Match object at ...>
      • 如果你想在string中任何位置找到匹配,使用search()。
    • split(string, maxsplit=0)
      • 和split()函数完全相同,使用编译的模式字符串。
    • findall(string[, pos[, endpos]])
      • 和findall()函数类似,使用已编译字符串,但也接受可选的pos和endpos可选参数,类似match()那样。
    • finditer(string[, pos[, endpos]])
      • 和finditer()函数类似,使用已编译的模式字符串,但也接受可选的pos和endpos可选参数,类似match()那样。
    • sub(repl, string, count=0)
      • 和sub()函数相同,使用已编译模式字符串。
    • subn(repl, string, count=0)
      • 和subn()函数相同,使用已编译模式字符串。
    • flags
      • 正则表达式匹配标志。这是由compile()和其他任意的(?…)pattern内联标志给定的组合。
    • groups
      • 捕获到的分组序号。
    • groupindex
      • 字典映射任意通过(?P<id>)定义的符号数组名到数组的序号。如果pattern没有给定符号组则字典为空。
    • pattern
      • RE对象编译的模式字符串。

        匹配对象

  • class re.MatchObject
    • 匹配对象永远有一个布尔值。match()和search()在未匹配到时会返回None,基于此你可以用一个if语句测试是否是一个匹配。
      match = re.search(pattern, string)
      if match:
        process(match)
      
    • 匹配对象支持下列的方法和属性:
    • expand(template)
      • 返回模板字符串template对反斜杠进行替换的字符串,就像在sub()方法中做的那样。转义字符串诸如\n会被转化成合适的字符,数字的反向引用(\1,\2)和命名反向引用(\g<1>,\g<name>)会被相应的匹配组内容替换。
    • group([group1, …])
      • 返回匹配的一到多个组。如果只有一个参数,结果就只是单个字符串;如果有多个参数,则结果是每个字符串组成的元组。没有参数时,group1默认为0(也就是匹配到的字符串本身)。如果groupN参数是0,对应的字符串就是整个字符串;如果在闭区间[1..99],匹配到的就是对应的那个小括号包起来的分组。如果组的号是负数,或者要大于模式字符串中定义的组的数量,会抛出一个IndexError。如果组包含在模式字符串的一部分且没有匹配到,对应的结果就是None。如果组在模式字符串的某部分且匹配了多次,则返回最后一次匹配到的结果。
        >>> m = re.match(r"(\w+) (\w+)", "Isaac Newton, physicist")
        >>> m.group(0)       # The entire match
        'Isaac Newton'
        >>> m.group(1)       # The first parenthesized subgroup.
        'Isaac'
        >>> m.group(2)       # The second parenthesized subgroup.
        'Newton'
        >>> m.group(1, 2)    # Multiple arguments give us a tuple.
        ('Isaac', 'Newton')
        
      • 如果正则表达式使用了(?P<name>...)语法,groupN参数也可以是用组名称识别组的字符串。如果字符串参数在模式字符串中没有用作组名字,抛IndexError。
      • 一个中庸的复杂例子:
        >>> m = re.match(r"(?P<first_name>\w+) (?P<last_name>\w+)", "Malcolm Reynolds")
        >>> m.group('first_name')
        'Malcolm'
        >>> m.group('last_name')
        'Reynolds'
        
      • 命名组也可以通过索引来引用:
        >>> m.group(1)
        'Malcolm'
        >>> m.group(2)
        'Reynolds'
        
      • 如果组匹配了多次,仅仅最后一次匹配到的字符串是可访问的:
        >>> m = re.match(r"(..)+", "a1b2c3")  # Matches 3 times.
        >>> m.group(1)                        # Returns only the last match.
        'c3'
        
    • groups([default])
      • 返回一个元组,包含所有的匹配到的组。default参数用于那些没有参与匹配的组;它默认是None。(不兼容注意:在1.5版本,如果元组只有一个元素,会返回一个字符串作为替代。在1.5.1后的版本,返回一个单一元素的元组。)
      • 例如:
        >>> m = re.match(r"(\d+)\.(\d+)", "24.1632")
        >>> m.groups()
        ('24', '1632')
        
      • 如果我们使用了数字位置和在其后可选的任何东西,不是所有组都参与匹配。这些组默认为None除非default参数是这样给定:
        >>> m = re.match(r"(\d+)\.?(\d+)?", "24")
        >>> m.groups()      # Second group defaults to None.
        ('24', None)
        >>> m.groups('0')   # Now, the second group defaults to '0'.
        ('24', '0')
        
    • groupdict([default])
      • 返回一个包含所有命名分组的字典,用组名做键。default参数用于那些没有参与匹配的组;默认是None。例如:
        >>> m = re.match(r"(?P<first_name>\w+) (?P<last_name>\w+)", "Malcolm Reynolds")
        >>> m.groupdict()
        {'first_name': 'Malcolm', 'last_name': 'Reynolds'}
        
    • start([group])
    • end([group])
      • 返回group组匹配的子串的起始和终止索引位置。group默认是0(意味着匹配的整个子串)。如果组存在但是没有匹配任何东西时返回-1。对一个匹配对象m,以及一个匹配到内容的组g来说,用组g匹配的子串(等同于m.group(g))就是:m.string[m.start(g):m.end(g)]
      • 注意到m.start(group)等同于m.end(group)如果组匹配了一个空串。例如,在m = re.search('b(c?)', 'cba')中,m.start(0)是1,m.end(0)是2,m.start(1)m.end(1)都是2,m.start(2)抛出IndexError异常。
      • 一个例子用于移除email地址的remove_this:
        >>> email = "tony@tiremove_thisger.net"
        >>> m = re.search("remove_this", email)
        >>> email[:m.start()] + email[m.end():]
        'tony@tiger.net'
        
    • span([group])
      • 对MatchObject m,返回2元组(m.start(group), m.end(group))。注意到如果group没有参与匹配,就是(-1,-1)。group默认为0,也就是整个匹配的串。
    • pos
      • pos的值用于传递给RegexObject的search()或match()方法。这是string的索引位置,用于RE引擎匹配的起始。
    • endpos
      • endpos的值用于传递给RegexObject的search()或match()方法。这是string的索引位置,用于RE引擎匹配的终止位置。
    • lastindex
      • 上一次匹配的捕获组的数字索引,没有匹配到任何组则为None。例如,表达式(a)b, ((a)(b)), 和 ((ab))在匹配字符串’ab’时lastindex为1,而对(a)(b)来说,lastindex就是2。
    • lastgroup
      • 上一次匹配的捕获组的名字,如果组没有名字,或者没有匹配到任何组时则为None。
    • re
      • 正则表达式对象,其match()或search()方法产生了这个MatchObject实例。
    • string
      • 传递给match()或search()的字符串。

例子

检查对

本例中,我们将使用下面的帮主函数来展示如何更优雅地匹配对象。

def displaymatch(match):
    if match is None:
        return None
    return '<Match: %r, groups=%r>' % (match.group(), match.groups())

假设你在写一个扑克牌程序,其中一个玩家的手里有5张牌,用五个字符的字符串表示,每个字符表示其中一张牌。’a’表示ace,’k’表示king,’q’表示queen,’j’表示jack,’t’表示10,’2’到’9’表示其对应的牌数字。

检查一个给定的串是否是合法的手牌,可以像下面这样做:

>>> valid = re.compile(r"^[a2-9tjqk]{5}$")
>>> displaymatch(valid.match("akt5q"))  # Valid.
"<Match: 'akt5q', groups=()>"
>>> displaymatch(valid.match("akt5e"))  # Invalid.
>>> displaymatch(valid.match("akt"))    # Invalid.
>>> displaymatch(valid.match("727ak"))  # Valid.
"<Match: '727ak', groups=()>"

最后的手牌,’727ak’包含一对,即两个相同的卡牌。可使用反斜杠来匹配这样的正则表达式。

>>> pair = re.compile(r".*(.).*\1")
>>> displaymatch(pair.match("717ak"))     # Pair of 7s.
"<Match: '717', groups=('7',)>"
>>> displaymatch(pair.match("718ak"))     # No pairs.
>>> displaymatch(pair.match("354aa"))     # Pair of aces.
"<Match: '354aa', groups=('a',)>"

为了找出包含的一对是哪个,我们可以用MatchObject的group()方法:

>>> pair.match("717ak").group(1)
'7'

# Error because re.match() returns None, which doesn't have a group() method:
>>> pair.match("718ak").group(1)
Traceback (most recent call last):
  File "<pyshell#23>", line 1, in <module>
    re.match(r".*(.).*\1", "718ak").group(1)
AttributeError: 'NoneType' object has no attribute 'group'

>>> pair.match("354aa").group(1)
'a'

仿真scanf()

python没有等同于scanf()的方法。正则表达式通常更有效,尽管在格式化字符串时也更为啰嗦。下表提供了或多或少的scanf()格式化token和正则表达式之间的等同映射。

scanf() Token 正则表达式
%c .
%5c .{5}
%d [-+]?\d+
%e,%E,%f,%g [-+]?(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?
%i [-+]?(0[xX][\daA-Fa-f]+|0[0-7]*|\d+)
%o [-+]?[0-7]+
%s \S+
%u \d+
%x, %X [-+]?(0[xX])?[\dA-Fa-f]+

压缩字符串如:

/usr/sbin/sendmail -0 errors, 4 warnings

你可以用scanf()这样格式化:

%s - %d errors, %d warnings

等同的正则表达式为:

(\S+) - (\d+) errors, (\d+) warnings

search() vs match()

python提供两个不同的操作,同样基于正则表达式:re.match()仅从字符串起始位置开始检查,而re.search()则检查任何位置的子串(这和Perl默认所做的处理相同)。

例如:

>>> re.match("c", "abcdef")    # No match
>>> re.search("c", "abcdef")   # Match
<_sre.SRE_Match object at ...>

正则表达式以’^’开始会强制search()在字符串起始处匹配。

>>> re.match("c", "abcdef")    # No match
>>> re.search("^c", "abcdef")  # No match
>>> re.search("^a", "abcdef")  # Match
<_sre.SRE_Match object at ...>

注意到无论MULTILINE是否开启,match()都仅仅匹配句首开始的串,而search()则在MULTILINE模式中匹配每行起始。

>>> re.match('X', 'A\nB\nX', re.MULTILINE)  # No match
>>> re.search('^X', 'A\nB\nX', re.MULTILINE)  # Match
<_sre.SRE_Match object at ...>

制作电话本

split()用分隔符分割字符串成列表。在转换字面数值成数据结构中这很有用,数据结构更易阅读,也更易被python修改,就像下面的一个例子,创建一个电话本所证明的那样。

首先,有一个输入。通常它从一个文件中得到,我们这里用三重引号语法:

>>> text = """Ross McFluff: 834.345.1254 155 Elm Street
...
... Ronald Heathmore: 892.345.3428 436 Finley Avenue
... Frank Burger: 925.541.7625 662 South Dogwood Way
...
...
... Heather Albrecht: 548.326.4584 919 Park Place"""

entries被分割成一或多行。我们转换字符串到一个列表,每个非空行包含他自己的条目:

>>> entries = re.split("\n+", text)
>>> entries
['Ross McFluff: 834.345.1254 155 Elm Street',
'Ronald Heathmore: 892.345.3428 436 Finley Avenue',
'Frank Burger: 925.541.7625 662 South Dogwood Way',
'Heather Albrecht: 548.326.4584 919 Park Place']

最后,分割每个entry到一个列表中,列表项分别是名字,姓,电话号,地址。我们使用maxsplit参数,因为地址有空白:

>>> [re.split(":? ", entry, 3) for entry in entries]
[['Ross', 'McFluff', '834.345.1254', '155 Elm Street'],
['Ronald', 'Heathmore', '892.345.3428', '436 Finley Avenue'],
['Frank', 'Burger', '925.541.7625', '662 South Dogwood Way'],
['Heather', 'Albrecht', '548.326.4584', '919 Park Place']]

:?模式字符串在姓之后匹配到冒号,因此不会出现在结果列表中。maxsplit如果为4的话,可以分离地址内街道名中的房子序号。

>>> [re.split(":? ", entry, 4) for entry in entries]
[['Ross', 'McFluff', '834.345.1254', '155', 'Elm Street'],
['Ronald', 'Heathmore', '892.345.3428', '436', 'Finley Avenue'],
['Frank', 'Burger', '925.541.7625', '662', 'South Dogwood Way'],
['Heather', 'Albrecht', '548.326.4584', '919', 'Park Place']]

文本改动

sub()替换字符串中匹配模式字符串的子串并作为结果返回。这一例子证实了使用sub()来修改文本,或随机化一句话的每个单词除了首尾的所有字母的顺序。

>>> def repl(m):
...     inner_word = list(m.group(2))
...     random.shuffle(inner_word)
...     return m.group(1) + "".join(inner_word) + m.group(3)
>>> text = "Professor Abdolmalek, please report your absences promptly."
>>> re.sub(r"(\w)(\w+)(\w)", repl, text)
'Poefsrosr Aealmlobdk, pslaee reorpt your abnseces plmrptoy.'
>>> re.sub(r"(\w)(\w+)(\w)", repl, text)
'Pofsroser Aodlambelk, plasee reoprt yuor asnebces potlmrpy.'

查找所有的副词

findall()匹配模式字符串匹配的所有地方,并不像search()那样。例如,一个作家想要找出一个文本的所有副词,那么可以用findall()这样写:

>>> text = "He was carefully disguised but captured quickly by police."
>>> re.findall(r"\w+ly", text)
['carefully', 'quickly']

查找所有的副词和它们的位置

如果想要查找更多的信息,finditer()更有帮助,他可以提供MatchObject的实例取代字符串。继续前面的例子,如果作家还想找出所有的副词的位置,那么可以用finditer():

>>> text = "He was carefully disguised but captured quickly by police."
>>> for m in re.finditer(r"\w+ly", text):
...     print '%02d-%02d: %s' % (m.start(), m.end(), m.group(0))
07-16: carefully
40-47: quickly

原始字符串记号

原始字符串记号(r"text")保持正则表达式的理智。如果没有它,反斜线’'会被认为是一个转义的前缀,例如下面的代码:

>>> re.match(r"\W(.)\1\W", " ff ")
<_sre.SRE_Match object at ...>
>>> re.match("\\W(.)\\1\\W", " ff ")
<_sre.SRE_Match object at ...>

当想要匹配一个字面意义反斜线时,必须在正则表达式中转义,对原始字符串来说,意味着r'\\',没有原始记号,则为\\\\,看下面的代码:

>>> re.match(r"\\", r"\\")
<_sre.SRE_Match object at ...>
>>> re.match("\\\\", r"\\")
<_sre.SRE_Match object at ...>