# 正規表示式(Regular Expression) ###### tags: `python` ## 介紹 **正規表示式**(英語:Regular Expression,常簡寫為regex、regexp或RE),又稱**正規表達式**、**正規表示法**、**規則運算式**、**常規表示法**。 正規表示式用來操作字串,透過某個規則(pattern)的來檢索、搜尋字串裡符合條件的文字。 所以也常用在對純文字的文件進行解析,例如:txt、html、xml、json檔案,從中萃取出所需要的文字,或是針對純文字檔案來進行處理。 Python 中做正規運算式的模組為 re ,首先要設定好「規則(pattern)」,並提供要進行處理的「字串 (string)」,然後在透過呼叫`re`模組中相關功能的函式(function)來進行處理。 > 提示: > 「規則(pattern)」通常會用使用Python的`r`開頭的原始字串(raw string)格式,這是因為正規表示式的規則中有符號跟Python字串中的跳脫符號會互相衝突(例如反斜線`\\`),所以必須使用原始字串來作為規則字串。 #### 網路資源 - Regular Expression測驗:https://regexone.com/ - Regular Expression測試:https://regex101.com/ ## 用途 1. 尋找資料(`findall`) 2. 驗證資料(`search`、`match`) 3. 抽取資料(`split`、`sub`) ## 常用re模組函數: | 函數 | 說明 | | -------------------------------------- | ------------------------------------------------------------ | | `findall(pattern, string)` | 回傳string中所有與pattern相匹配的全部字串,返回形式為陣列。 | | `finditer(pattern, string)` | 回傳string中所有與pattern相匹配的全部字串,返回形式為迭代器。 | | `search(pattern, string)` | 回傳從string中「第一個」包含pattern的字串 ,沒有找到則回傳None 。 | | `match(pattern, string)` | 匹配字串的開頭,如果有包含pattern,則匹配成功,回傳Match物件,失敗則回傳None。<br/>若要完全匹配pattern,必須以$結尾。 | | `fullmatch(pattern, string)` | 判斷 string 是否與配對形式字串 pattern 完全相符,如果完全相符就回傳配對物件,不完全相符就回傳 None 。 | | `compile(pattern)` | 以pattern字串當參數,回傳 re.compile() 物件,提供其他支援正規表示式的函式使用。 | | `split(pattern, string, maxsplit=0)` | 將 string 以配對形式字串 pattern 拆解,結果回傳拆解後的串列。 | | `sub(pattern, repl, string, count=0)` | 依據 pattern 及 repl 對 string 進行處理,結果回傳處理過的新字串。 | | `subn(pattern, repl, string, count=0)` | 依據 pattern 及 repl 對 string 進行處理,結果回傳處理過的序對。 | | `escape(pattern)` | 將 pattern 中的特殊字元加入反斜線,結果回傳新字串。 | | `purge()` | 清除正規運算式的內部緩存。 | > 備註: > > **re.match()與re.search()的差別** > > re.match只有匹配字串的開頭,如果字符串開頭就不符合正則表達式,則匹配失敗,函式回傳None;而re.search()則是整個字串都會做匹配,只要找到一個匹配就表示成功,整個字串都沒有匹配才會回傳None。 ## 中介字元(Metacharacters) 說明: | 中介字元 | 說明 | 範例 |說明| | -------- | ------------------------------------------------------------ |------ |------ | | [] | 字元的集合。 |[a-m]|a~m之間的小寫英文字| | \ | 發出特殊序列的信號(也可以用於轉義特殊字符)。 |\d|只要數字| | . | 除了新行符號外的任意字元。 |he..o|he字串後接著兩個字元,然後接著是o| | ^ | 字串以此為開頭。 |^hello|字串開頭為hello| | $ | 以此為結尾的字串。 |world$|字串結尾為world| | * | 字元或字串出現任意次數(包含0次)。 |aix*|ai、aix、aix和aixx或更多x都符合。| | ? | 字元或字串出現 0 或 1 次。 |aix?|僅ai、aix符合。| | + | 字元或字串至少出現一次。 |aix+|僅aix符合。| | {m,n} | 指定字元或字串出現的m~n之間的次數。 |al{2}<br/>al{3,6}|a後面連續2個l的字串<br/>a後面連續3到6個l的字串| | \| | 單一字元或群組的或,例如 'a\|b' 為 'a' 或 'b' 。 |falls\|stays|字串包含falls或是stays| | () | 對小括弧內的字元形成群組。 ||| ## 特別序列(Special Sequences) 說明: | 特別序列 | 說明 | | -------- | -------------------------------- | | \A | 字串的開頭字元。 | | \b | 單字的界線字元。 | | \B | 字元的界線字元。 | | \d | 數字,從 0 到 9 。 | | \D | 非數字。 | | \s | 各種空白符號,包含換行符號 \n 。 | | \S | 非空白符號。 | | \w | 任意文字字元,包括數字。 | | \W | 非文字字元,包括空白符號。 | | \Z | 字串的結尾字元。 | > 補充: > > `\A`、`\Z`和`^`、`$`有類似的作用,差別在於前者會以全部內容為主,後者會以換行為結束。 ### `findall()` #### 範例ㄧ:找出a~m之間的小寫英文字 ```python= import re txt = 'The rain in Spain' x = re.findall(r'[a-m]', txt) print(x) ``` 輸出: ``` ['h', 'e', 'a', 'i', 'i', 'a', 'i'] ``` #### 範例二:找出數字 ```python= import re txt = 'That will be 59 dollars' x = re.findall(r'\d', txt) print(x) ``` ``` ['5', '9'] ``` #### 範例三:找出he字串後接著兩個字元,然後接著是o ```python= import re txt = 'hello world' x = re.findall('he..o', txt) print(x) ``` 輸出: ``` ['hello'] ``` #### 範例四:字串開頭必須為hello ```python= import re txt = 'hello world' x = re.findall(r'^hello', txt) if x: print("Yes, the string starts with 'hello'") else: print('No match') ``` 輸出: ``` Yes, the string starts with 'hello' ``` #### 範例五:字串結尾為world ```python= import re txt = 'hello world' x = re.findall(r'world$', txt) if x: print("'Yes, the string ends with 'world'") else: print('No match') ``` 輸出: ``` Yes, the string ends with 'world' ``` #### 範例六:找出ai字串後面有0~多個x字元的字串 ```python= import re txt = 'The rain in Spain falls mainly in the plain!' x = re.findall(r'aix*', txt) print(x) if x: print('Yes, there is at least one match!') else: print('No match') ``` 輸出: ``` ['ai', 'ai', 'ai', 'ai'] Yes, there is at least one match! ``` #### 範例七:找出ai字串後面有1~多個x字元的字串 ```python= import re txt = 'The rain in Spain falls mainly in the plain!' x = re.findall(r'aix+', txt) print(x) if x: print('Yes, there is at least one match!') else: print('No match') ``` 輸出: ``` [] No match ``` #### 範例八:找出a後面連續2個l的字串 ```python= import re txt = 'The rain in Spain falls mainly in the plain!' x = re.findall(r'al{2}', txt) print(x) if x: print('Yes, there is at least one match!') else: print('No match') ``` 輸出: ``` ['all'] Yes, there is at least one match! ``` #### 範例九:字串包含falls或是stays ```python= import re txt = 'The rain in Spain falls mainly in the plain!' #Check if the string contains either 'falls' or 'stays': x = re.findall(r'falls|stays', txt) print(x) if x: print('Yes, there is at least one match!') else: print('No match') ``` 輸出: ``` ['falls'] Yes, there is at least one match! ``` ### ### `search()` #### 找出第一個空白字元的位置 ```python= import re txt = 'The rain in Spain' x = re.search(r'\s', txt) print(r'The first white-space character is located in position:', x.start()) ``` 輸出: ``` The first white-space character is located in position: 3 ``` #### 找出Portugal是否出現在字串中 ```python= import re txt = 'The rain in Spain' x = re.search(r'Portugal', txt) print(x) ``` 輸出: ``` None ``` ### `split` #### 使用空白字元分割字串。 ```python= import re txt = 'The rain in Spain' x = re.split(r'\s', txt) print(x) ``` 輸出: ``` ['The', 'rain', 'in', 'Spain'] ``` #### 使用空白字元分割字串,並限制最大分割次數。 ```python= import re #Split the string at the first white-space character: txt = 'The rain in Spain' x = re.split(r'\s', txt, 1) print(x) ``` ``` ['The', 'rain in Spain'] ``` ### `sub()` #### 使用9取代所有的空白字元: ```python= import re #Replace all white-space characters with the digit '9': txt = 'The rain in Spain' x = re.sub(r'\s', '9', txt) print(x) ``` 輸出: ``` The9rain9in9Spain ``` #### 使用9取代所有的空白字元,並限制最大的取代次數: ```python= import re #Replace the first two occurrences of a white-space character with the digit 9: txt = 'The rain in Spain' x = re.sub(r'\s', '9', txt, 2) print(x) ``` 輸出: ``` The9rain9in Spain ``` ## 集合範例 | 集合 | 說明 | | :--------- | :----------------------------------------------------------- | | \[arn\] | 回傳字串中含有a、r或n的小寫字元。 | | \[a-n\] | 回傳字串中含有a~n之間的任意小寫字元。 | | \[^arn\] | 回傳任意字元,除了,a、r和n。 | | [0123] | 回傳字串中含有0、1、2或3的數字。 | | \[0-9\] | 回傳字串中含有0~9之間的數字。 | | \[0-5\]\[0-9\] | 回傳00~59之間的數字。 | | \[a-zA-Z\] | 回傳a~z之間的大寫和小寫字元。 | | \[+\] | 回傳字串中的+號(`+`, `*`, `.`, `|`, `()`, `$`,`{}`沒有特殊作用,只是單純代表+號)。 | ## 比較`match`、`searh`、`findall`、`finditer`差異 | | match | search | findall | finditer | | -------- | ----------------------------------------------- | ------------------------------------- | --------------------------------- | --------------------------------------- | | 說明 | 字串開頭開始,如果包含pattern子字串則成功 | 整個字串中只要有出現pattern字串就成功 | 回傳字串中所有符合pattern的子字串 | 回傳字串中所有符合pattern的子字串迭代器 | | 成功回傳 | Match物件 | Match物件 | 清單 | Match物件迭代器 | | 失敗回傳 | None | None | 空清單 | 空迭代器 | | 其它 | 如果要整個字串符合pattern,則pattern必須是$結尾 | | | | ## 應用 - 以正規表示式來搜尋網頁屬性 #### 利用正規表達式來找出多個符合條件的標籤: ```python= from bs4 import BeautifulSoup import re html_doc = ''' <html> <head> <title>這是HTML文件標題</title> </head> <body> <div id='item-1'>牛肉乾</div> <div id='item-2'>豬肉乾</div> <div id='item-3'>羊肉乾</div> <div id='item-4'>鳥肉乾</div> <div id='item-5'>雞肉乾</div> </body> </html> ''' # 建立BeautifulSoup物件解析HTML文件 soup = BeautifulSoup(html_doc, 'lxml') items = soup.find_all(id=re.compile(r'^item')) for i in items: print(i) ``` 輸出: ``` <div id='item-1'>牛肉乾</div> <div id='item-2'>豬肉乾</div> <div id='item-3'>羊肉乾</div> <div id='item-4'>鳥肉乾</div> <div id='item-5'>雞肉乾</div> ``` #### 驗證手機號碼 1. 一共有 10 位數 2. 開頭要是 09 3. 每一個字元都要是數字 ``` ^09\d{8}$ ``` ##### 範例: ```python= import re phones = ['0912345678', '023456789', '096312345'] for p in phones: result = re.findall(r'^09\d{8}$', p) if len(result) > 0: print(p + '是手機號碼') else: print(p + '不是手機號碼') ``` ## 練習 ㄧ、將下面的email清單,只抽取出帳號的部分,其餘去掉: ``` aaronho@gmail.com andyliu@yahoo.com apple@google.com abner@microsoft.com amberok@facebook.com ``` 只留下: ``` aaronho andyliu apple abner amberok ``` 二、以下哪些字串可以配對到這個 RE:`/\w\w\w.\d\d\d/`。 ``` 1. 000000 2. 9999999 3. aaaaaaa 4. 0a0a000 5. 0a0a0a0 6. cc3c777 7. cccc777 ``` 三、找出下面文章包含tion和sion的單字: ``` A regular expression (shortened as regex or regexp;also referred to as rational expression) is a sequence of characters that define a search pattern. Usually such patterns are used by string-searching algorithms for "find" or "find and replace" operations on strings, or for input validation. It is a technique developed in theoretical computer science and formal language theory. ``` #### 答案 ㄧ、 ``` @.+$ ``` 二、 ``` 9999999 0a0a000 cc3c777 cccc777 ``` 三、 ```python= import re txt = ''' A regular expression (shortened as regex or regexp;also referred to as rational expression) is a sequence of characters that define a search pattern. Usually such patterns are used by string-searching algorithms for "find" or "find and replace" operations on strings, or for input validation. It is a technique developed in theoretical computer science and formal language theory. ''' result = re.findall(r'\w*tion\w*|\w*sion\w*', txt) print(result) ```