Python正则表达式操作指南(5)(转载)

财经 网编 2023-03-03 09:58 192 0

  修改字符串

  到目前为止,我们简单地搜索了一个静态字符串。正则表达式通常也用不同的方式,通过下面的 `RegexObject` 方法,来修改字符串。

  方法/属性 作用

  split() 将字符串在 RE 匹配的地方分片并生成一个列表,

  sub() 找到 RE 匹配的所有子串,并将其用一个不同的字符串替换

  subn() 与 sub() 相同,但返回新的字符串和替换次数

  将字符串分片

  `RegexObject` 的 split() 方法在 RE 匹配的地方将字符串分片,将返回列表。它同字符串的 split() 方法相似但提供更多的定界符;split()只支持空白符和固定字符串。就象你预料的那样,也有一个模块级的 re.split() 函数。

  split(string [, maxsplit = 0])

  通过正则表达式将字符串分片。如果捕获括号在 RE 中使用,那么它们的内容也会作为结果列表的一部分返回。如果 maxsplit 非零,那么最多只能分出 maxsplit 个分片。

  你可以通过设置 maxsplit 值来限制分片数。当 maxsplit 非零时,最多只能有 maxsplit 个分片,字符串的其馀部分被做为列表的最後部分返回。在下面的例子中,定界符可以是非数字字母字符的任意序列。

  #!python

  >>> 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 中使用,那么它们的值也会当作列表的一部分返回。比较下面的调用:

  #!python

  >>> 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 作为第一个参数,其他一样。

  #!python

  >>> 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.']

  搜索和替换

  其他常见的用途就是找到所有模式匹配的字符串并用不同的字符串来替换它们。sub() 方法提供一个替换值,可以是字符串或一个函数,和一个要被处理的字符串。

  sub(replacement, string[, count = 0])

  返回的字符串是在字符串中用 RE 最左边不重复的匹配来替换。如果模式没有发现,字符将被没有改变地返回。

  可选参数 count 是模式匹配後替换的最大次数;count 必须是非负整数。缺省值是 0 表示替换所有的匹配。

  这里有个使用 sub() 方法的简单例子。它用单词 "colour" 替换颜色名。

  #!python

  >>> 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() 方法作用一样,但返回的是包含新字符串和替换执行次数的两元组。

  #!python

  >>> 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)

  空匹配只有在它们没有紧挨着前一个匹配时才会被替换掉。

  #!python

  >>> p = re.compile('x*')

  >>> p.sub('-', 'abxd')

  '-a-b-d-'

  如果替换的是一个字符串,任何在其中的反斜杠都会被处理。"\n" 将会被转换成一个换行符,"\r"转换成回车等等。未知的转义如 "\j" 是 left alone。逆向引用,如 "\6",被 RE 中相应的组匹配而被子串替换。这使你可以在替换後的字符串中插入原始文本的一部分。

  这个例子匹配被 "{" 和 "}" 括起来的单词 "section",并将 "section" 替换成 "subsection"。

  #!python

  >>> p = re.compile('section{ ( [^}]* ) }', re.VERBOSE)

  >>> p.sub(r'subsection{\1}','section{First} section{second}')

  'subsection{First} subsection{second}'

  还可以指定用 (?P<name>...) 语法定义的命名组。"\g<name>" 将通过组名 "name" 用子串来匹配,并且 "\g<number>" 使用相应的组号。所以 "\g<2>" 等于 "\2",但能在替换字符串里含义不清,如 "\g<2>0"。("\20" 被解释成对组 20 的引用,而不是对後面跟着一个字母 "0" 的组 2 的引用。)

  #!python

  >>> 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}'

  替换也可以是一个甚至给你更多控制的函数。如果替换是个函数,该函数将会被模式中每一个不重复的匹配所调用。在每个调用时,函数被作为 `MatchObject` 的匹配函属,并可以使用这个信息去计算预期的字符串并返回它。

  在下面的例子里,替换函数将十进制翻译成十六进制:

  #!python

  >>> 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() 函数时,模式作为第一个参数。模式也许是一个字符串或一个 `RegexObject`;如果你需要指定正则表达式标志,你必须要么使用 `RegexObject` 做第一个参数,或用使用模式内嵌修正器,如 sub("(?i)b+", "x", "bbbb BBBB") returns 'x x'。

  常见问题

  正则表达式对一些应用程序来说是一个强大的工具,但在有些时候它并不直观而且有时它们不按你期望的运行。本节将指出一些最容易犯的常见错误。

  使用字符串方式

  有时使用 re 模块是个错误。如果你匹配一个固定的字符串或单个的字符类,并且你没有使用 re 的任何象 IGNORECASE 标志的功能,那么就没有必要使用正则表达式了。字符串有一些方法是对固定字符串进行操作的,它们通常快很多,因为都是一个个经过优化的C 小循环,用以代替大的、更具通用性的正则表达式引擎。

  举个用一个固定字符串替换另一个的例子;如,你可以把 "deed" 替换成 "word"。re.sub() seems like the function to use for this, but consider the replace() method. 注意 replace() 也可以在单词里面进行替换,可以把 "swordfish" 变成 "sdeedfish",不过 RE 也是可以做到的。(为了避免替换单词的一部分,模式将写成 \bword\b,这是为了要求 "word" 两边有一个单词边界。这是个超出替换能力的工作)。

  另一个常见任务是从一个字符串中删除单个字符或用另一个字符来替代它。你也许可以用象 re.sub('\n',' ',S) 这样来实现,但 translate() 能够实现这两个任务,而且比任何正则表达式操作起来更快。

  总之,在使用 re 模块之前,先考虑一下你的问题是否可以用更快、更简单的字符串方法来解决。

  match() vs search()

  match() 函数只检查 RE 是否在字符串开始处匹配,而 search() 则是扫描整个字符串。记住这一区别是重要的。记住,match() 只报告一次成功的匹配,它将从 0 处开始;如果匹配不是从 0 开始的,match() 将不会报告它。

  #!python

  >>> print re.match('super', 'superstition').span()

  (0, 5)

  >>> print re.match('super', 'insuperable')

  None

  另一方面,search() 将扫描整个字符串,并报告它找到的第一个匹配。

  #!python

  >>> print re.search('super', 'superstition').span()

  (0, 5)

  >>> print re.search('super', 'insuperable').span()

  (2, 7)

  有时你可能倾向于使用 re.match(),只在RE的前面部分添加 .* 。请尽量不要这么做,最好采用 re.search() 代替之。正则表达式编译器会对 REs 做一些分析以便可以在查找匹配时提高处理速度。一个那样的分析机会指出匹配的第一个字符是什么;举个例子,模式 Crow 必须从 "C" 开始匹配。分析机可以让引擎快速扫描字符串以找到开始字符,并只在 "C" 被发现後才开始全部匹配。

  添加 .* 会使这个优化失败,这就要扫描到字符串尾部,然後回溯以找到 RE 剩馀部分的匹配。使用 re.search() 代替。

  贪婪 vs 不贪婪

  当重复一个正则表达式时,如用 a*,操作结果是尽可能多地匹配模式。当你试着匹配一对对称的定界符,如 HTML 标志中的尖括号时这个事实经常困扰你。匹配单个 HTML 标志的模式不能正常工作,因为 .* 的本质是“贪婪”的

  #!python

  >>> 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 匹配 在 "" 中的 "<",.* 消耗掉子符串的剩余部分。在 RE 中保持更多的左,虽然 > 不能匹配在字符串结尾,因此正则表达式必须一个字符一个字符地回溯,直到它找到 > 的匹配。最终的匹配从 "<" 到 "" 中的 ">",这并不是你所想要的结果。在这种情况下,解决方案是使用不贪婪的限定符 *?、+?、?? 或 {m,n}?,尽可能匹配小的文本。在上面的例子里, ">" 在第一个 "<" 之后被立即尝试,当它失败时,引擎一次增加一个字符,并在每步重试 ">"。这个处理将得到正确的结果:

  #!python

  >>> print re.match('<.*?>', s).group()

  本文源自:ht tp://w w w.c svt.ne t/

评论区