首頁  >  文章  >  後端開發  >  比較詳細Python正規表示式操作指南

比較詳細Python正規表示式操作指南

巴扎黑
巴扎黑原創
2017-05-21 11:16:381649瀏覽

Python 自1.5版本起增加了re 模块,它提供 Perl 风格的正则表达式模式。Python 1.5之前版本则是通过 regex 模块提供 Emecs 风格的模式。Emacs 风格模式可读性稍差些,而且功能也不强,因此编写新代码时尽量不要再使用 regex 模块,当然偶尔你还是可能在老代码里发现其踪影。

就其本质而言,正则表达式(或 RE)是一种小型的、高度专业化的编程语言,(在Python中)它内嵌在Python中,并通过 re 模块实现。使用这个小型语言,你可以为想要匹配的相应字符串集指定规则;该字符串集可能包含英文语句、e-mail地址、TeX命令或任何你想搞定的东西。然後你可以问诸如“这个字符串匹配该模式吗?”或“在这个字符串中是否有部分匹配该模式呢?”。你也可以使用 RE 以各种方式来修改或分割字符串。


正则表达式模式被编译成一系列的字节码,然後由用 C 编写的匹配引擎执行。在高级用法中,也许还要仔细留意引擎是如何执行给定 RE ,如何以特定方式编写 RE 以令生产的字节码运行速度更快。本文并不涉及优化,因为那要求你已充分掌握了匹配引擎的内部机制。


正则表达式语言相对小型和受限(功能有限),因此并非所有字符串处理都能用正则表达式完成。当然也有些任务可以用正则表达式完成,不过最终表达式会变得异常复杂。碰到这些情形时,编写 Python 代码进行处理可能反而更好;尽管 Python 代码比一个精巧的正则表达式要慢些,但它更易理解。

简单模式

我们将从最简单的正则表达式学习开始。由于正则表达式常用于字符串操作,那我们就从最常见的任务:字符匹配 下手。


有关正则表达式底层的计算机科学上的详细解释(确定性和非确定性有限自动机),你可以查阅编写编译器相关的任何教科书。

字符匹配

大多数字母和字符一般都会和自身匹配。例如,正则表达式 test 会和字符串“test”完全匹配。(你也可以使用大小写不敏感模式,它还能让这个 RE 匹配“Test”或“TEST”;稍後会有更多解释。)

这个规则当然会有例外;有些字符比较特殊,它们和自身并不匹配,而是会表明应和一些特殊的东西匹配,或者它们会影响到 RE 其它部分的重复次数。本文很大篇幅专门讨论了各种元字符及其作用。

这里有一个元字符的完整列表;其含义会在本指南馀下部分进行讨论。

^ $ * + ? { [ ] \ | ( )

我们首先考察的元字符是"[" 和 "]"。它们常用来指定一个字符类别,所谓字符类别就是你想匹配的一个字符集。字符可以单个列出,也可以用“-”号分隔的两个给定字符来表示一个字符区间。例如,[abc] 将匹配"a", "b", 或 "c"中的任意一个字符;也可以用区间[a-c]来表示同一字符集,和前者效果一致。如果你只想匹配小写字母,那幺 RE 应写成 [a-z].

元字符在类别里并不起作用。例如,[akm$]将匹配字符"a", "k", "m", 或 "$" 中的任意一个;"$"通常用作元字符,但在字符类别里,其特性被除去,恢复成普通字符。

你可以用补集来匹配不在区间范围内的字符。其做法是把"^"作为类别的首个字符;其它地方的"^"只会简单匹配 "^"字符本身。例如,[^5] 将匹配除 "5" 之外的任意字符。

也许最重要的元字符是反斜杠"""。 做为 Python 中的字符串字母,反斜杠後面可以加不同的字符以表示不同特殊意义。它也可以用于取消所有的元字符,这样你就可以在模式中匹配它们了。举个例子,如果你需要匹配字符 "[" 或 """,你可以在它们之前用反斜杠来取消它们的特殊意义: "[ 或 ""。

一些用 """ 开始的特殊字符所表示的预定义字符集通常是很有用的,象数字集,字母集,或其它非空字符集。下列是可用的预设特殊字符:

\d  匹配任何十进制数;它相当于类 [0-9]。

\D  匹配任何非数字字符;它相当于类 [^0-9]。

\s  匹配任何空白字符;它相当于类  [ "t"n"r"f"v]。

\S  匹配任何非空白字符;它相当于类 [^ "t"n"r"f"v]。

\w  匹配任何字母数字字符;它相当于类 [a-zA-Z0-9_]。

\W  匹配任何非字母数字字符;它相当于类 [^a-zA-Z0-9_]。

这样特殊字符都可以包含在一个字符类中。如,["s,.]字符类将匹配任何空白字符或","或"."。

本节最後一个元字符是 . 。它匹配除了换行字符外的任何字符,在 alternate 模式(re.DOTALL)下它甚至可以匹配换行。"." 通常被用于你想匹配“任何字符”的地方。

重复

正则表达式第一件能做的事是能够匹配不定长的字符集,而这是其它能作用在字符串上的方法所不能做到的。 不过,如果那是正则表达式唯一的附加功能的话,那么它们也就不那么优秀了。它们的另一个功能就是你可以指定正则表达式的一部分的重复次数。

我们讨论的第一个重复功能的元字符是 *。* 并不匹配字母字符 "*";相反,它指定前一个字符可以被匹配零次或更多次,而不是只有一次。

举个例子,ca*t 将匹配 "ct" (0 个 "a" 字符), "cat" (1 个 "a"), "caaat" (3 个 "a" 字符)等等。RE 引擎有各种来自 C 的整数类型大小的内部限制,以防止它匹配超过2亿个 "a" 字符;你也许没有足够的内存去建造那么大的字符串,所以将不会累计到那个限制。

象 * 这样地重复是“贪婪的”;当重复一个 RE 时,匹配引擎会试着重复尽可能多的次数。如果模式的後面部分没有被匹配,匹配引擎将退回并再次尝试更小的重复。


一步步的示例可以使它更加清晰。让我们考虑表达式 a[bcd]*b。它匹配字母 "a",零个或更多个来自类 [bcd]中的字母,最後以 "b" 结尾。现在想一想该 RE 对字符串 "abcbd" 的匹配。

Step Matched Explanation
1 a a 匹配模式
2 abcbd 引擎匹配 [bcd]*,并尽其所能匹配到字符串的结尾
3 Failure 引擎尝试匹配 b,但当前位置已经是字符的最後了,所以失败
4 abcb 退回,[bcd]*尝试少匹配一个字符。
5 Failure 再次尝次b,但在当前最後一位字符是"d"。
6 abc 再次退回,[bcd]*只匹配 "bc"。
7 abcb 再次尝试 b ,这次当前位上的字符正好是 "b"

RE 的结尾部分现在可以到达了,它匹配 "abcb"。这证明了匹配引擎一开始会尽其所能进行匹配,如果没有匹配然後就逐步退回并反复尝试 RE 剩下来的部分。直到它退回尝试匹配 [bcd] 到零次为止,如果随後还是失败,那么引擎就会认为该字符串根本无法匹配 RE 。


另一个重复元字符是 +,表示匹配一或更多次。请注意 * 和 + 之间的不同;*匹配零或更多次,所以根本就可以不出现,而 + 则要求至少出现一次。用同一个例子,ca+t 就可以匹配 "cat" (1 个 "a"), "caaat" (3 个 "a"), 但不能匹配 "ct"。


还有更多的限定符。问号 ? 匹配一次或零次;你可以认为它用于标识某事物是可选的。例如:home-?brew 匹配 "homebrew" 或 "home-brew"。


最复杂的重复限定符是 {m,n},其中 m 和 n 是十进制整数。该限定符的意思是至少有 m 个重复,至多到 n 个重复。举个例子,a/{1,3}b 将匹配 "a/b","a//b" 和 "a///b"。它不能匹配 "ab" 因为没有斜杠,也不能匹配 "a////b" ,因为有四个。


你可以忽略 m 或 n;因为会为缺失的值假设一个合理的值。忽略 m 会认为下边界是 0,而忽略 n 的结果将是上边界为无穷大 -- 实际上是先前我们提到的 2 兆,但这也许同无穷大一样。


细心的读者也许注意到其他三个限定符都可以用这样方式来表示。 {0,} 等同于 *,{1,} 等同于 +,而{0,1}则与 ? 相同。如果可以的话,最好使用 *,+,或?。很简单因为它们更短也再容易懂。

使用正则表达式

现在我们已经看了一些简单的正则表达式,那么我们实际在 Python 中是如何使用它们的呢? re 模块提供了一个正则表达式引擎的接口,可以让你将 REs 编译成对象并用它们来进行匹配。

编译正则表达式

正则表达式被编译成 `RegexObject` 实例,可以为不同的操作提供方法,如模式匹配搜索或字符串替换。

#!python

>>> import re

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

>>> print p

<re.RegexObject instance at 80b4150>

re.compile() 也接受可选的标志参数,常用来实现不同的特殊功能和语法变更。我们稍後将查看所有可用的设置,但现在只举一个例子:

#!python

>>> p = re.compile('ab*', re.IGNORECASE)

RE 被做為一個字串傳送給 re.compile()。 REs 被處理成字串是因為正規表示式不是 Python 語言的核心部分,也沒有為它建立特定的語法。 (應用程式根本不需要 REs,因此沒必要包含它們去使語言說明變得臃腫不堪。)而 re 模組則只是以一個 C 擴展模組的形式來被 Python 包含,就像 socket 或 zlib 模組一樣。


將 REs 作為字串以確保 Python 語言的簡潔,但這樣帶來的一個麻煩就是像下節標題所講的。

反斜線的麻煩

在早期規定中,正規表示式用反斜線字元(""") 來表示特殊格式或允許使用特殊字元而不呼叫它的特殊用法。可能是在一個LATEX 檔案查找。為了要在程式碼中判斷,首先要寫出想要匹配的字串。接下來你需要在所有反斜線和元字元前加上反斜線來取消其特殊意義。


字元階段#\section要匹配的字串\\section為re.compile 取消反斜線的特殊意義"\\\\section"為字串取消反斜線簡單地說,為了匹配一個反斜杠,不得不在RE 字串中寫'\ \',因為正規表示式中必須是"\\",而每個反斜線按Python 字串字母表示的常規必須表示成"\\"。在 REs 中反斜杠的這個重複特性會導致大量重複的反斜杠,而且所產生的字串也很難理解。


解決的方法就是為正規表示式使用Python 的raw 字串表示;在字串前加個"r" 反斜線就不會被任何特殊方式處理,所以r"\ n" 是包含"\" 和"n" 的兩個字符,而"\n" 則是一個字符,表示一個換行。正規表示式通常在 Python 程式碼中都是用這種 raw 字串表示。


常規字串Raw 字串"ab*"r "ab*""\\\\section"r"\\section""\\ w+\\s+\\1"r"\w+\s+\1"#執行符合
#一旦你有了已經編譯了的正規表示式的對象,你要用它做什麼呢? `RegexObject` 實例有一些方法和屬性。這裡只顯示了最重要的幾個,如果要看完整的清單請查閱Python Library Reference

#方法/屬性作用match()決定RE 是否在字串剛開始的位置符合search()掃描字串,找到這個RE 匹配的位置findall()#找到RE 匹配的所有子字串,並將它們作為一個列表返回finditer()找到RE 相符的所有子字串,並將它們當作一個迭代器傳回


如果沒有符合的話,match() 和 search() 將會回傳 None。如果成功的話,就會傳回一個 `MatchObject` 實例,其中有這次匹配的資訊:它是從哪裡開始和結束,它所匹配的子字串等等。

你可以用採用人機對話並用 re 模組實驗的方式來學習它。如果你有 Tkinter 的話,也許可以考慮參考一下 Tools/scripts/redemo.py,一個包含在 Python 發行版裡的示範程式。

首先,執行Python 解譯器,匯入re 模組並編譯一個RE:

#!python

Python 
2.2.2 (#1, Feb 10 2003, 12:57:01)

>>> import re

>>> p = re.compile(' [a-z]+')

#>>>o





##!python

#############>>>###### p.match(############################################################################# #""######)############>>>###### ######print###### p.match (######""######)######None#############現在,讓我們試著用它來匹配一個字串,如" tempo"。這時,match() 將會傳回一個 MatchObject。因此你可以將結果保存在變數裡以便後面使用。 ###

## !python

>>> m = p.match( 'tempo')

>> > print m

<_sre.SRE_Match object at 80c4f68>

現在你可以查詢`MatchObject` 關於符合字串的相關資訊了。 MatchObject 實例也有幾個方法和屬性;最重要的那些如下所示:

傳回符合結束的位置傳回一個元組包含符合(開始,結束) 的位置
#方法/屬性 作用
#group() 傳回被RE 匹配的字串
##end()
span()


試試這些方法不久就會清楚它們的作用了:

#!python

> >> m.group()

'tempo##'







#>>>

 m.start(), m.end()


##!python
#>>> print p.match('::: message'
)
None#>>> m 
#=
 p.search(##'





# #::: message') ; 

#print

### m############<## ####re.MatchObject instance at 80c9650#######>##################>>>####### m. group()############'######message######'#################> >>###### m.span()######(#######4######, #######11#######) #########在實際程式中,最常見的作法是將`MatchObject` 保存在一個變數裡,然後檢查它是否為None,通常如下所示:###

## !python

#= re.compile(  )

#m 
= p.match( '#string goes here' )

if m:

#print ##'Match found: ', m.group()

#else##:

print
 'No match'兩個 `RegexObject` 方法傳回所有符合模式的子字串。 findall()傳回一個符合字串行表:



##!python

!python >>> p #= re.compile(

'
#" d+')>>> p.findall(

#'
12 drummers drumming, 11 pipers piping, 10 lords a-leaping'#['#'##'#'

## ###12######'######, ######'#######11######'#######, ##### ##'######10######'######]##########findall() 在它傳回結果時不得不建立一個清單。中,也可以用finditer() 方法。

#!python

>>> iterator = p.finditer('12 drummers drumming, 11  10 ')

>>> iterator

<callable-iterator object at 0x401833ac>

>>> for match in iterator:

     
print match.span()



(0, 
2)

(
2224)

(
2931)

模块级函数

你不一定要产生一个 `RegexObject` 对象然後再调用它的方法;re 模块也提供了顶级函数调用如 match()、search()、sub() 等等。这些函数使用 RE 字符串作为第一个参数,而後面的参数则与相应 `RegexObject` 的方法参数相同,返回则要么是 None 要么就是一个 `MatchObject` 的实例。

#!python

>>> print re.match(r'From"s+''Fromage amk')

None

>>> re.match(r'From"s+''From amk Thu May 14 19:12:10 1998')

<re.MatchObject instance at 80c5978>

Under the hood, 这些函数简单地产生一个 RegexOject 并在其上调用相应的方法。它们也在缓存里保存编译後的对象,因此在将来调用用到相同 RE 时就会更快。


你将使用这些模块级函数,还是先得到一个 `RegexObject` 再调用它的方法呢?如何选择依赖于怎样用 RE 更有效率以及你个人编码风格。如果一个 RE 在代码中只做用一次的话,那么模块级函数也许更方便。如果程序包含很多的正则表达式,或在多处复用同一个的话,那么将全部定义放在一起,在一段代码中提前编译所有的 REs 更有用。从标准库中看一个例子,这是从 xmllib.py 文件中提取出来的:

#!python

ref 
= re.compile(  )

entityref 
= re.compile(  )

charref 
= re.compile(  )

starttagopen 
= re.compile(  )

我通常更喜欢使用编译对象,甚至它只用一次,but few people will be as much of a purist about this as I am。

编译标志

编译标志让你可以修改正则表达式的一些运行方式。在 re 模块中标志可以使用两个名字,一个是全名如 IGNORECASE,一个是缩写,一字母形式如 I。(如果你熟悉 Perl 的模式修改,一字母形式使用同样的字母;例如 re.VERBOSE的缩写形式是 re.X。)多个标志可以通过按位 OR-ing 它们来指定。如 re.I | re.M 被设置成 I 和 M 标志:


这有个可用标志表,对每个标志後面都有详细的说明。

标志 含义
DOTALL, S 使 . 匹配包括换行在内的所有字符
IGNORECASE, I 使匹配对大小写不敏感
LOCALE, L 做本地化识别(locale-aware)匹配
MULTILINE, M 多行匹配,影响 ^ 和 $
VERBOSE, X 能够使用 REs 的 verbose 状态,使之被组织得更清晰易懂

I
IGNORECASE

使匹配对大小写不敏感;字符类和字符串匹配字母时忽略大小写。举个例子,[A-Z]也可以匹配小写字母,Spam 可以匹配 "Spam", "spam", 或 "spAM"。这个小写字母并不考虑当前位置。

L
LOCALE

影响 "w, "W, "b, 和 "B,这取决于当前的本地化设置。

locales 是 C 语言库中的一项功能,是用来为需要考虑不同语言的编程提供帮助的。举个例子,如果你正在处理法文文本,你想用 "w+ 来匹配文字,但 "w 只匹配字符类 [A-Za-z];它并不能匹配 "é" 或 "ç"。如果你的系统配置适当且本地化设置为法语,那么内部的 C 函数将告诉程序 "é" 也应该被认为是一个字母。当在编译正则表达式时使用 LOCALE 标志会得到用这些 C 函数来处理 "w 後的编译对象;这会更慢,但也会象你希望的那样可以用 "w+ 来匹配法文文本。

M
MULTILINE


(此时 ^ 和 $ 不会被解释; 它们将在 4.1 节被介绍.)


使用 "^" 只匹配字符串的开始,而 $ 则只匹配字符串的结尾和直接在换行前(如果有的话)的字符串结尾。当本标志指定後, "^" 匹配字符串的开始和字符串中每行的开始。同样的, $ 元字符匹配字符串结尾和字符串中每行的结尾(直接在每个换行之前)。

S
DOTALL

使 "." 特殊字符完全匹配任何字符,包括换行;没有这个标志, "." 匹配除了换行外的任何字符。

X
VERBOSE


该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解。当该标志被指定时,在 RE 字符串中的空白符被忽略,除非该空白符在字符类中或在反斜杠之後;这可以让你更清晰地组织和缩进 RE。它也可以允许你将注释写入 RE,这些注释会被引擎忽略;注释用 "#"号 来标识,不过该符号不能在字符串或反斜杠之後。


举个例子,这里有一个使用 re.VERBOSE 的 RE;看看读它轻松了多少?

#!python

charref 
= re.compile(r"""

&[[]]             # Start of a numeric entity reference

(

[0-9]+[^0-9]      # Decimal form

| 0[0-7]+[^0-7]   # Octal form

| x[0-9a-fA-F]+[^0-9a-fA-F] # Hexadecimal form

)

""", re.VERBOSE)

没有 verbose 设置, RE 会看起来象这样:

#!python

charref 
= re.compile("([0-9]+[^0-9]"

"|0[0-7]+[^0-7]"

"|x[0-9a-fA-F]+[^0-9a-fA-F])")

在上面的例子里,Python 的字符串自动连接可以用来将 RE 分成更小的部分,但它比用 re.VERBOSE 标志时更难懂。

更多模式功能

到目前为止,我们只展示了正则表达式的一部分功能。在本节,我们将展示一些新的元字符和如何使用组来检索被匹配的文本部分。

更多的元字符

還有一些我們還沒展示的元字符,其中的大部分將在本節展示。


剩下來要討論的部分元字元是零寬界定符(zero-width assertions)。它們並不會使引擎在處理字串時更快;相反,它們根本就沒有對應任何字符,只是簡單的成功或失敗。舉個例子, "b 是一個在單字邊界定位目前位置的界定符(assertions),這個位置根本就不會被 "b 改變。這意味著零寬界定符(zero-width assertions)將永遠不會被重複,因為如果它們在給定位置匹配一次,那麼它們很明顯可以被匹配無數次。

|


可選項,或 "or" 操作符。如果 A 和 B 是正規表示式,A|B 將符合任何符合了 "A" 或 "B" 的字串。 | 的優先權非常低,是為了當你有多字串要選擇時能適當地運行。 Crow|Servo 將會符合"Crow" 或 "Servo", 而非 "Cro", 一個 "w" 或 一個 "S", 和 "ervo"。


為了符合字母"|",可以用"|,或將其包含在字元類別中,如[|]。

^


符合行首。 除非設定MULTILINE 標誌,它只是符合字串的開始。 #例如,如果你只希望匹配在行首單字"From",那么RE 將用^From。 //www.CodeHighlighter.com/

-->

#!python#!python

>>> print re.search('}$''{block}')

<re.MatchObject instance at 80adfa8>

>>> print re.search('}$''{block} ')

None

>>> print re.search('}$''{block}"n')

<re.MatchObject instance at 80adfa8>

匹配一个 "$",使用 "$ 或将其包含在字符类中,如[$]。

"A


只匹配字符串首。当不在 MULTILINE 模式,"A 和 ^ 实际上是一样的。然而,在 MULTILINE 模式里它们是不同的;"A 只是匹配字符串首,而 ^ 还可以匹配在换行符之後字符串的任何位置。

"Z

Matches only at the end of the string.
只匹配字符串尾。

"b

单词边界。这是个零宽界定符(zero-width assertions)只用以匹配单词的词首和词尾。单词被定义为一个字母数字序列,因此词尾就是用空白符或非字母数字符来标示的。


下面的例子只匹配 "class" 整个单词;而当它被包含在其他单词中时不匹配。

## !python

>>> p = re.compile(r'"bclass"b'#)

##> ;>> print p.search('no class at all')

<#re.MatchObject instance at 80c8f28#>

#>>> #print p.search(##'#the declassified algorithm')None



>>>
 print p.search('one subclass is')None

當用這個特殊序列時你應該記住這裡有兩個微妙之處。第一個是 Python 字串和正規表示式之間最糟糕的衝突。在Python 字串裡,""b" 是反斜線字符,ASCII值是8。如果你沒有使用raw 字串時,那么Python 會把""b" 轉換成一個回退符,你的RE將無法像你希望的那樣匹配它了。下面的例子看起來和我們前面的 RE 一樣,但在 RE 字串前少了一個 "r" 。

## !python

>>> p = re.compile('"bclass"b#')

> >> print p.search('no class at all# ')

None

>>> print ## p.search('"b'

 
+ 



##!python

################>>>###### p ########## ##### re.compile(######'######(ab)*######'######)########## ##>>>###### ######print####### p.match(######'######ababababab###### #'######).span()######(0, #######10######)######

组用 "(" 和 ")" 来指定,并且得到它们匹配文本的开始和结尾索引;这就可以通过一个参数用 group()、start()、end() 和 span() 来进行检索。组是从 0 开始计数的。组 0 总是存在;它就是整个 RE,所以 `MatchObject` 的方法都把组 0 作为它们缺省的参数。稍後我们将看到怎样表达不能得到它们所匹配文本的 span。

#!python

>>> p = re.compile('(a)b')

>>> m = p.match('ab')

>>> m.group()

'ab'

>>> m.group(0)

'ab'

小组是从左向右计数的,从1开始。组可以被嵌套。计数的数值可以能过从左到右计算打开的括号数来确定。

#!python

>>> p = re.compile('(a(b)c)d')

>>> m = p.match('abcd')

>>> m.group(0)

'abcd'

>>> m.group(1)

'abc'

>>> m.group(2)

'b'

group() 可以一次输入多个组号,在这种情况下它将返回一个包含那些组所对应值的元组。

## !python

>>> m.group(##2,1,2)

(
'b''#abc'#, #'b')

The groups() 方法傳回一個包含所有群組字串的元組,從 
#1 到 所包含的小組號碼。

#!python

#>>> ; m.groups()

(
'#abc'#, #'b')

#模式中的逆向引用允許你指定先前捕獲群組的內容,該群組也必須在字串目前位置被找到。舉個例子,如果群組1 的內容能夠在當前位置找到的話,"1 就成功否則失敗。記住Python 字串也是用反斜線加資料來允許字串中包含任意字元的,所以當在RE 中使用逆向引用時確保使用raw 字串。 by Actipro CodeHighlighter (freeware)

http://www.CodeHighlighter.com/


-->

#!python

>>> m = re.match("([abc])+""abc")

>>> m.groups()

(
'c',)

>>> m = re.match("(?:[abc])+""abc")

>>> m.groups()

()

除了捕获匹配组的内容之外,无捕获组与捕获组表现完全一样;你可以在其中放置任何字符,可以用重复元字符如 "*" 来重复它,可以在其他组(无捕获组与捕获组)中嵌套它。(?:...) 对于修改已有组尤其有用,因为你可以不用改变所有其他组号的情况下添加一个新组。捕获组和无捕获组在搜索效率方面也没什么不同,没有哪一个比另一个更快。


其次,更重要和强大的是命名组;与用数字指定组不同的是,它可以用名字来指定。


指令群組的語法是 Python 專用擴充之一: (?P...)。名字很明顯是組的名字。除了該組有個名字之外,命名組也同捕獲組是相同的。 `MatchObject` 的方法處理捕獲組時接受的要么是表示組號的整數,要么是包含組名的字串。命名群組也可以是數字,所以你可以用兩種方式來得到一個群組的資訊:

#!python



## >>> p =# re.compile(r
'
##(?P "b"w+"b)
')#>>> m  ##=

 p.search( 
'(((( Lots of punctuation )))#' )

>>> m.group('word

'
)'

Lots

#'

#!python

InternalDate 
#= re.compile(r'INTERNALDATE "

'
#r'(?P [ 123][0-9])-(?P[A-Z][a-z][a-z])-

'
r '(?P[0-9][0-9][0-9][0-9])

'
r' (?P[0-9][0-9]):(?P[0-9][ 0-9]):(?P[0-9][0-9])

#'

######r#####' ###### (?P[-+])(?P[0-9][0-9])(?P[0-9][0-9] )######'############r######'######"######'#######)# ########很明顯,得到m.group('zonem') 要比記住得到組9 要容易得多。 ###


因为逆向引用的语法,象 (...)"1 这样的表达式所表示的是组号,这时用组名代替组号自然会有差别。还有一个 Python 扩展:(?P=name) ,它可以使叫 name 的组内容再次在当前位置发现。正则表达式为了找到重复的单词,("b"w+)"s+"1 也可以被写成 (?P"b"w+)"s+(?P=word):

#!python

>>> p = re.compile(r'(?P"b"w+)"s+(?P=word)')

>>> p.search('Paris in the the spring').group()

'the the'

前向界定符

另一个零宽界定符(zero-width assertion)是前向界定符。前向界定符包括前向肯定界定符和後向肯定界定符,所下所示:

(?=...)

前向肯定界定符。如果所含正则表达式,以 ... 表示,在当前位置成功匹配时成功,否则失败。但一旦所含表达式已经尝试,匹配引擎根本没有提高;模式的剩馀部分还要尝试界定符的右边。

(?!...)

前向否定界定符。与肯定界定符相反;当所含表达式不能在字符串当前位置匹配时成功


通过示范在哪前向可以成功有助于具体实现。考虑一个简单的模式用于匹配一个文件名,并将其通过 "." 分成基本名和扩展名两部分。如在 "news.rc" 中,"news" 是基本名,"rc" 是文件的扩展名。


匹配模式非常简单:

.*[.].*$

注意 "." 需要特殊对待,因为它是一个元字符;我把它放在一个字符类中。另外注意後面的 $; 添加这个是为了确保字符串所有的剩馀部分必须被包含在扩展名中。这个正则表达式匹配

"foo.bar""autoexec.bat"、 "sendmail.cf" 和 "printers.conf"

现在,考虑把问题变得复杂点;如果你想匹配的扩展名不是 "bat" 的文件名?一些不正确的尝试:

.# *[.][^b].#*$

上面的第一次移除"bat" 的嘗試是要求擴展名的第一個字元不是"b"。這是錯誤的,因為該模式也不能符合 "foo.bar"。

. *[.]([^b]..##|.[ ^a].|..[^t])$

#當你試著修補第一個解決方法而要求匹配下列情況之一時表達式更亂了:擴展名的第一個字元不是"b"; 第二個字元不是"a";或第三個字元不是"t"。這樣可以接受 "foo.bar" 而拒絕 "autoexec.bat",但這要求只能是三個字元的副檔名而不接受兩個字元的副檔名如 "sendmail.cf"。我們將在努力修補它時再次把該模式變得複雜。


. *[.]([^b].?.?##|##.[^a]?.?##|..?[##^t]? )$在第三次嘗試中,第二和第三個字母都變成可選,為的是允許匹配比三個字元更短的擴展名,如"sendmail. cf"。 該模式現在變得非常複雜,這使它很難讀懂。更糟的是,如果問題改變了,你想要副檔名不是 "bat" 和 "exe",這個模式甚至會變得更複雜和混亂。

前向否定把所有這些裁切成:


Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter. com/

-->.# *[.](?!bat$|exe$).##*##$#修改字串

到目前為止,我們簡單地搜尋了一個靜態字串。正規表示式通常也用不同的方式,透過下面的 `RegexObject` 方法,來修改字串。

方法/屬性#split()sub()subn()

将字符串分片

`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('這是對split() 的一個簡短而有趣的測試。'## ## #)######[######'##是'
, 
'a##', '測試', '#short' ,## ####'######甜美######' #, #'#of##' '分割'#, ''] >>>> p.split('這是一個split() 的測試,簡短而有趣。', 3)#['##''
#'
, ' a''#split() 的簡短而甜蜜的測試。 ]

有時,你不僅對定界符之間的文字感興趣,還需要知道定界符回傳是什麼。下面的值是目前列表的一部分。

## !python

>>> p = re.compile(r'"W+#')

##> >> p2 =# re.compile(r'("W+)')

>>> p.split(' #This is a test.')
[
'## ''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.']#搜尋與取代
其他常見的用途就是找到所有模式匹配的字串並用不同的字串來替換它們。

Code highlighting produced by Actipro CodeHighlighter (freeware)

http://www.CodeHighlighter.com/


sub(replacement,#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

'

)##!python

#>>>
 p  re.compile('x*#') >>>
 p.sub(
'#-'


################################### '######abxd######'######)#############'######-a-b-d-###### '##################如果替換的是字串,任何在其中的反斜線都會被處理。 ""n" 將會轉換成一個換行符,""r"轉換成回車等等。未知的轉義如 ""j" 則保持原樣。逆向引用,如 ""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...) 语法定义的命名组。""g" 将通过组名 "name" 用子串来匹配,并且 ""g" 使用相应的组号。所以 ""g<2>" 等于 ""2",但能在替换字符串里含义不清,如 ""g<2>0"。(""20" 被解释成对组 20 的引用,而不是对後面跟着一个字母 "0" 的组 2 的引用。)

## !python

>>> p = re.compile('section{ (?P [^}]* ) }', 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}','section{First}#')

'subsection{First}'
##替換也可以是一個甚至給你更多控制的函數。 ` 的符合函屬,並且可以使用這個資訊去計算預期的字串並傳回它。

## !python

>>> 









# # hexrepl( match ):     "

Return the hex string for a decimal number


##H

##HH




############################################HH 但########### 從」 ########     value ######=###### int( match.group() )##########     ##### ## ## 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` 做第一個參數,或用使用模式內嵌修正器,如sub("(?i)b+", "x", "bbbb BBBB") returns 'x x'。 ###常見問題######正規表示式對某些應用程式來說是一個強大的工具,但在有些時候它並不直觀而且有時它們不按你期望的運行。容易犯的常見錯誤。象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 =' Title'

#>>># len(s)

32

>>> print



 re.match('<.*>'

, s).span()
#(0, 32)#>>> #print re.match('<.*>', s).group()

<<span style="font-family:新宋体">html</span>><

head
##><

title#!python

#pat 
= re.compile(r"""

#"s*                 # Skip leading:whitespace
1&P> ]+)   # Header name

"s* :               # Whitespace, and a  -- *? used to

# lose the following trailing whitespace

"s*$                # , re.VERBOSE)


這個要難讀得多:

Code highlighting produced by Actipro CodeHighlighter (freeware) http://www .CodeHighlighter.com/ -->

#!python

作用
將字串在RE 匹配的地方分片並產生一個列表,
#找到RE 匹配的所有子字串,並將其用不同的字串替換
與sub() 相同,但傳回新的字串和替換次數

以上是比較詳細Python正規表示式操作指南的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn