# 正規表達式(python) ## 基本符號 符號 | 意義 | 用法 ------ |----------------------------------------------- |------------ . | 換行以外的任何字元 | . ? | 符合0次或1次,或者比對最少的就停止。 | a? \* | 符合0次或多次,或者盡可能比對最多。 | a* \+ | 符合1次或多次。 | a+ [] | 自訂比對格式。左側中括號加上^表示[]內的除外。 | [abc] [^abc] {} | 指定比對的次數。 | a{1,5} a{1,} a{,5} () | 將規則分組。 | (abc) ^ | 表示開頭。 | ^abc $ | 表示結尾。 | abc$ \d | 從0到9的數字,大寫D表示\d除外 | \d \D \w | 任何的字母、數字及底線符號_,大寫W表示\w除外 | \w \W \s | 空白字元,包括空格、(tab)、換行符號,大寫S表示\s除外 | \s \S ## 使用正規表達式 python的用法需要引入一個re模組 ``` import re ``` 使用compile建立物件正規表達式物件,表達式: ``` Regex = re.compile(r'\d{2}-\d{4}-\d{4}') ``` 字串前加上r的意思是保留原始字串,避免與python其他功能衝突,例如跳脫字元 Regex物件可以用幾個函式來做不同的搜尋: 方法 | 功能 ---------|------ match | 尋找必須是開頭的字串(相當於正規表達式開頭加上^) search | 尋找第一個符合的字串 findall | 尋找所有符合的字串 finditer | 尋找所有符合的字串 findall會把找到的值以串列方式回傳,沒找到則返回空串列 match、search、finditer是回傳物件,沒找到則返回none,要以以下方法取值: 方法 | 功能 ---------|------ gruop | 找到的值 span | 找到值的起始位置與結束位置(回傳數組) start | 找到值的起始位置 end | 找到值的結束位置 ### 使用search ``` Regex = re.compile(r'\d{2}-\d{4}-\d{4}') str = "Please call David at 02-8888-1688 by today.\r\n02-9888-9898 is his office number." result = Regex.search(str) print("group():",result.group()) print("span():",result.span()) print("start():",result.start()) print("end():",result.end()) ``` 輸出: ![](https://i.imgur.com/SZXnSY1.png) ### 使用match ``` Regex = re.compile(r'\d{2}-\d{4}-\d{4}') str = "Please call David at 02-8888-1688 by today.\r\n02-9888-9898 is his office number." result = Regex.match(str) print(result) ``` 輸出: ![](https://i.imgur.com/x2VGx8J.png) ``` Regex = re.compile(r'\d{2}-\d{4}-\d{4}') str = "02-9888-9898 is his office number." result = Regex.match(str) print(result) ``` 輸出: ![](https://i.imgur.com/RPn4fKr.png) ### 使用findall ``` Regex = re.compile(r'\d{2}-\d{4}-\d{4}') str = "Please call David at 02-8888-1688 by today.\r\n02-9888-9898 is his office number." result = Regex.findall(str) print(result) ``` 輸出: ![](https://i.imgur.com/3gaWZJ7.png) ### 使用finditer ``` Regex = re.compile(r'\d{2}-\d{4}-\d{4}') str = "Please call David at 02-8888-1688 by today.\r\n02-9888-9898 is his office number." result = Regex.finditer(str) for match in result: print("group():",match.group()) print("span():",match.span()) print("start():",match.start()) print("end():",match.end()) print() ``` 輸出: ![](https://i.imgur.com/xv5x7wD.png) ## 練習 1. 判斷是否為台灣的電話(0911111111,886911111111) 2. 判斷是否為台灣身份證字號(英文大寫字母+1 or 2+八個數字) 3. 判斷格式是否符合:1~3組數字,每組數字佔兩位,並以逗號分隔 ## 進階 ### 更多符號 符號 | 意義 :------ |:------ &#124; | 可以比對多個規則,較前面的條件符合為比對結果 \A | 字串開頭,等同於^,但不受MULTILINE旗標影響 \Z | 字串結尾,等同於$,但不受MULTILINE旗標影響 \b | 表示單字邊界,即單字開頭與結尾 ``` ``` ### 旗標 旗標 | 功能 ------------- | -------- ASCII, A | 使用 ASCII 字符集 UNICODE, U | 使用 Unicode 字符集 DOTALL, S | .可以表示任何字元,包括換行字元 IGNORECASE, I | 忽略大小寫 LOCALE, L | 匹配 {\w \W \b \B} 跟本地語言相關。不推薦使用 MULTILINE, M | ^、$將換行作為字串開始與結尾 VERBOSE, X | 忽略各種空格以及以#開頭的註釋,這使得長匹配模式可以分行來寫,提高了 可讀性 #### 旗標範例 要使用旗標時將re."旗標名"寫到compile函數後面,並以逗號分隔 寫完整單字或是字母縮寫都可以 ``` Regex = re.compile(r'[ABC]',re.IGNORECASE) or Regex = re.compile(r'[ABC]',re.I) ``` ##### MULTILINE ``` Regex = re.compile(r'^\w+',re.M) str = "Please call David at 02-8888-1688 by today.\r\n02-9888-9898 is his office number." result = Regex.findall(str) print(result) ``` 輸出: ![](https://i.imgur.com/1oHmtLw.png) ##### VERBOSE 下列以電子郵件的正規表達式示範 沒VERBOSE旗標: ``` Regex = re.compile(r"^([\w!#$%&'*+\-/=?^_`{|}~]+)(\.[\w!#$%&'*+\-/=?^_`{|}~]+)*@[\w-]+(\.[\w-]+)+$") ``` 有VERBOSE旗標: ``` Regex = re.compile(r""" ^ ([\w!#$%&'*+\-/=?^_`{|}~]+) #第一個點前的字串 (\.[\w!#$%&'*+\-/=?^_`{|}~]+)* #任意組點加字串 @ [\w-]+ #一組字串 (\.[\w-]+)+ #至少一組點加字串 $ """,re.X)) ``` ### 分组 被()包括的內容就是一個組,有時候需要再尋找更細節的資料,就可以分組來取得細節 可以發現在原本的正規表達式中再加入了三組括號,分別將三組數字再個別分組,之後輸出在函數中加入參數取值 函數的參數(0)等同於(),就是完整正規表達式所找到的字串 甚至可以以逗號串接引數,將會返回一組數組 ``` Regex = re.compile(r'(\d{2})-(\d{4})-(\d{4})') str = "Please call David at 02-8888-1688 by today.\r\n02-9888-9898 is his office number." result = Regex.search(str) print(result.group(0),result.span(0)) print(result.group(1),result.span(1)) print(result.group(2),result.span(2)) print(result.group(3),result.span(3)) print(result.group(0,1,2,3)) ``` 輸出: ![](https://i.imgur.com/LauzZvT.png) 使用findall若正規表達式中有分組,則只輸出組的內容,想要完整字串只要全部再用括號包起來即可 ``` Regex = re.compile(r'(\d{2})-(\d{4})-(\d{4})') str = "Please call David at 02-8888-1688 by today.\r\n02-9888-9898 is his office number." result = Regex.findall(str) print(result) ``` 輸出: ![](https://i.imgur.com/Ch9eb5d.png) 若不想在使用findall的時候搜尋到括號的內容,就在括號內的開頭加上?: ``` Regex = re.compile(r'((\d{2})-(\d{4})-(?:\d{4}))') str = "Please call David at 02-8888-1688 by today.\r\n02-9888-9898 is his office number." result = Regex.findall(str) print(result) ``` 輸出: ![](https://i.imgur.com/KoaajIh.png) (? )這類型的用法還有很多例如: 1. a(?=b) a後面必須是b ``` Regex = re.compile(r'\d{4}(?=-1688)') str = "Please call David at 02-8888-1688 by today.\r\n02-9888-9898 is his office number." result = Regex.findall(str) print(result) ``` 輸出: ![](https://i.imgur.com/rMfX3ks.png) 需要注意的是放在(?= )裡的條件不會出現在結果裡 2. a(?!b) a後面必須不是b ``` Regex = re.compile(r'\d{4}(?!-1688)') str = "Please call David at 02-8888-1688 by today.\r\n02-9888-9898 is his office number." result = Regex.findall(str) print(result) ``` 結果: ![](https://i.imgur.com/lTfvnFo.png) 3. (?P\<first\>...) 把分組命名為first\ 除了能做為group等函數的參數外,用groupdict函數還能做為key輸出成字典 ``` Regex = re.compile(r'(?P<first>\d{2})-(?P<second>\d{4})-(?P<third>\d{4})') str = "Please call David at 02-8888-1688 by today.\r\n02-9888-9898 is his office number." result = Regex.search(str) print("group()\t\t\t:",result.group()) print("group(\"first\")\t:",result.group("first")) print("group(\"second\")\t:",result.group("second")) print("group(\"third\")\t:",result.group("third")) print("groupdict()\t\t:",result.groupdict()) ``` 輸出: ![](https://i.imgur.com/b2AVW2j.png)