# 練習 31:正則表達式
> 原文:[Exercise 31: Regular Expressions](https://learncodethehardway.org/more-python-book/ex31.html)
> 譯者:[飛龍](https://github.com/wizardforcel)
> 協議:[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)
> 自豪地采用[谷歌翻譯](https://translate.google.cn/)
正則表達式(RegEx)是一種簡潔的方式,用于確定字符序列應如何在字符串中匹配。通常大家都認為它們是“可怕”的,但是,正如你所知道的,任何包含在恐懼中的東西通常都不是這樣。正則表達式的事實是,它們是大約八個符號的集合,告訴計算機如何匹配模式串。簡單來說,他們很容易理解。人們遇到困難的地方是,嘗試使用難以置信的復雜的正則表達式,其中解析器實際上會更好。一旦你明白了這八個符號和正則表達式的限制,你就會看到它們根本不可怕。
我打算讓你記憶更多東西,使你的的大腦為討論做好準備。
> `^`
> **錨定字符串開頭**。只有字符串剛好位于開頭,它才會匹配。
> `$`
> **錨定字符串末尾**。只有字符串到達了末尾,它才會匹配。
> `.`
> **任何單個字符**。接受任何單個字符的輸入。
> `?`
> 正則表達式的**之前的部分是可選的**,所以`A?`的意思是可選的字符`A`。
> `*`
> 之前的部分是零個或多個(**任意個**)。選取正則表達式的之前的部分,重復接受或者跳過它。`A*`會接受`"AAAAAAA"`或者`"BQEFT"`,因為它里面有零個`A`。
> `+`
> 之前的部分是一個或多個(**至少一個**)。和`*`類似,但是只接受一個或多個這種字符。`A+`會匹配`"AAAAAAA"`,但不是`"BQEFT"`。
> `[X-Y]`
> `X`到`Y`的**字符范圍**,接受任何范圍中列出的字符串。`[A-Z]`表示所有大寫英文字母。許多常見字符范圍擁有`\`快捷方式,你可以使用它來代替。
> `()`
> **捕獲**這個正則表達式的部分,便于稍后使用。許多正則表達式庫將其用于替換、提取或修改文本。捕獲會選取正則表達式的`()`中的部分,并保存它便于以后使用。之后許多庫可以讓你引用這些捕獲。如果你使用`([A-Z]+)`,它會捕獲一個或多個大寫英文單詞。
Python 的[`re`庫](https://docs.python.org/3/library/re.html)列出了一些更多的符號,但大多都是這八個的一些修飾符,或者不在正則表達式庫中經常發現的額外功能。你將快速記住這八個來起步,重點是粗體的部分(錨定末尾,之前部分可選),以便你可以快速回憶它們并解釋他們的作用。
記住這些符號后,請查看以下正則表達式并將其翻譯成中文,并使用 Python `re`庫來嘗試列出的字符串,或你可以想到的任何其他字符串。
> `".*BC?$"`
> `helloBC`, `helloB`, `helloA`, `helloBCX`
> `"[A-Za-z][0-9]+"`
> `A1232344`, `abc1234`, `12345`, `b493034`
> `"^[0-9]?a*b?.$"`
> `0aaaax`, `aaab9`, `9x`, `88aabb`, `9zzzz`
> `"A+B+C+[xyz]*"`
> `AAAABBCCCCCCxyxyz`, `ABBBBCCCxxxx`, `ABABABxxxx`
一旦你翻譯了它們,使用Python `re`模塊,嘗試在 Shell 中嘗試它們,如下:
```py
>>> import re
>>> m = re.match(r".*BC?$", "helloB").span()
>>> re.match(r".*BC?$", "helloB").span()
(0, 6)
>>> re.match(r"[A-Za-z][0-9]+", "A1232344").span()
(0, 8)
>>> re.match(r"[A-Za-z][0-9]+", "abc1234").span()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'span'
>>> re.match(r"[A-Za-z][0-9]+", "1234").span()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'span'
>>> re.match(r"[A-Za-z][0-9]+", "b493034").span()
(0, 7)
>>>
```
對于任何不匹配,你會得到`AttributeError: 'NoneType'`,因為當你的正則表達式不匹配時,`re.match`函數返回`None`。
## 挑戰練習
挑戰是嘗試使用你的 FSM 模塊來實現一個簡單的正則表達式,至少執行三個操作。這將是一個困難的挑戰,但使用 Python `re`庫來幫助你規劃和測試此正則表達式的實現。然后,一旦你知道如何實現它,永遠不要這樣做了。人生苦短,不要做計算機已經擅長的事情。
## 研究性學習
+ 擴展你的記憶,來包括 Python `re`庫文檔中的所有可能的符號。
+ 如果你想要匹配一個`*`字符,那么你可以用`\*`來轉義它。大多數其他符號也有類似的東西。
+ 確保你知道如何使用`re.ASCII`,因為某些解析的需求需要它。
## 深入學習
看看[`regex`庫](https://pypi.python.org/pypi/regex/),如果你需要 Unicode 支持,那么這個更好。
- 笨辦法學 Python · 續 中文版
- 引言
- 第一部分:預備知識
- 練習 0:起步
- 練習 1:流程
- 練習 2:創造力
- 練習 3:質量
- 第二部分:簡單的黑魔法
- 練習 4:處理命令行參數
- 練習 5:cat
- 練習 6:find
- 練習 7:grep
- 練習 8:cut
- 練習 9:sed
- 練習 10:sort
- 練習 11:uniq
- 練習 12:復習
- 第三部分:數據結構
- 練習 13:單鏈表
- 練習 14:雙鏈表
- 練習 15:棧和隊列
- 練習 16:冒泡、快速和歸并排序
- 練習 17:字典
- 練習 18:性能測量
- 練習 19:改善性能
- 練習 20:二叉搜索樹
- 練習 21:二分搜索
- 練習 22:后綴數組
- 練習 23:三叉搜索樹
- 練習 24:URL 快速路由
- 第四部分:進階項目
- 練習 25:xargs
- 練習 26:hexdump
- 練習 27:tr
- 練習 28:sh
- 練習 29:diff和patch
- 第五部分:文本解析
- 練習 30:有限狀態機
- 練習 31:正則表達式
- 練習 32:掃描器
- 練習 33:解析器
- 練習 34:分析器
- 練習 35:解釋器
- 練習 36:簡單的計算器
- 練習 37:小型 BASIC
- 第六部分:SQL 和對象關系映射
- 練習 38:SQL 簡介
- 練習 39:SQL 創建
- 練習 40:SQL 讀取
- 練習 41:SQL 更新
- 練習 42:SQL 刪除
- 練習 43:SQL 管理
- 練習 44:使用 Python 的數據庫 API
- 練習 45:創建 ORM
- 第七部分:大作業
- 練習 46:blog
- 練習 47:bc
- 練習 48:ed
- 練習 49:sed
- 練習 50:vi
- 練習 51:lessweb
- 練習 52:moreweb