iT邦幫忙

2

【python內建模組- re】使用正規表達式配對指定格式的字串

大家好,今天要分享的是一個python好用的內建模組-re
其實有想學這個模組很久了,
不過小馬之前一直都看不懂這個模組到底要怎麼使用,
最近不斷反覆咀嚼學習才終於大致理解這個模組在做什麼

簡介- 什麼是正則表達式?

其實正則表達式並不是python自己發明的,
很多程式語言都有,
主要是用來做模式的比對與搜尋

動機- 搜索特定格式的文字

譬如說台灣的手機號碼的格式為xxxx-xxx-xxx
我們要如何從一個字串裡面有沒有包含台灣手機號碼格式的子字串呢?
這邊要引用書本上的程式,
我會將資料來源放在參考資料

(利用傳統知識對字串格式做判斷)

# 引用 Python入門邁向高手之路王者歸來/ 出版社:深石/ 作者: 洪錦魁 一書,
# 第16章第一節的程式
def taiwanPhoneNum(string):
    """檢查是否有含手機聯絡資訊的台灣手機號碼格式"""
    if len(string) != 12:       # 如果長度不是12
        return False            # 傳回非手機號碼格式
    
    for i in range(0, 4):       # 如果前4個字出現非數字字元
        if string[i].isdecimal() == False:
            return False        # 傳回非手機號碼格式
        
    if string[4] != '-':        # 如果不是'-'字元
        return False            # 傳回非手機號碼格式
   
    for i in range(5, 8):       # 如果中間3個字出現非數字字元
        if string[i].isdecimal() == False:
            return False        # 傳回非手機號碼格

    if string[8] != '-':        # 如果不是'-'字元
        return False            # 傳回非手機號碼格式

    for i in range(9, 12):      # 如果最後3個字出現非數字字元
        if string[i].isdecimal() == False:
            return False        # 傳回非手機號碼格
    return True                 # 通過以上測試

這個函數可以判斷字串是否滿足台灣電話號碼的格式,
可以看到說這樣搜索要判斷的細節非常多,
如果哪天改成判斷中國、美國、或外星人電話號碼的格式,
那麼重新設計程式大概也蠻耗時的

但…如果懂正則表達式語法的話,
那麼台灣電話號碼格式其實可以簡單表示為
r'\d\d\d\d-\d\d\d-\d\d\d'或是
r'\d{4}-\d{3}-\d{3}'

\d是配對數字字元0~9的意思

本文小馬不打算細寫正則表達式的語法,
因為細節真的很多

而且別人既然寫過了,
好像也沒必要再自己重寫 (彷彿免費教大家又無酬勞(誤~)),
不過附上不錯的參考資料供大家學習

參考資料

  1. (書本) Python入門邁向高手之路王者歸來/ 出版社:深石/ 作者: 洪錦魁
    https://ithelp.ithome.com.tw/upload/images/20200301/20117114iS8wGZXAWv.png

學習正則表達式的話首推這本書,書中將使用正則表達式的動機、語法、細節都介紹的蠻清楚的,雖然一開始沒接觸過可能很不習慣,但是反覆閱讀咀嚼大致能看懂。搭配練習實作會更熟悉的。

  1. python官方文檔: re正則表達式操作

官網有對於語法的說明,但似乎沒有像書本中講的那麼清楚好懂

小馬自己的練習題

由於光看不練還是很難學會re模組,
分享自己練習的題目:
參考題目: CodeWar- 5kyu Sorting Poker
題意: 給你一個字串(譬如說D6H2S3D5SJCQSKC7D2C5H5H10SA),表示撲克牌的手牌,依指定順序幫撲克牌排序

  • 撲克牌的點數順序為2 3 4 5 6 7 8 9 10 J Q K A
  • 撲克牌的花色有S, D, H, C (黑桃、方塊、紅心、梅花)

那麼問題來了,
我要如何把字串D6H2S3D5SJCQSKC7D2C5H5H10SA分離成一張一張的撲克牌呢?
(例如轉成一個列表,每個元素就是一張牌['D6', 'H2', 'S3', 'D5', 'SJ', 'CQ', 'SK', 'C7', 'D2', 'C5', 'H5', 'H10', 'SA'],這樣才方便排序)
這時,我發現用正則表達式的話可以很方便的解決這個問題,
每張牌的格式一定是(英文字母+數字1~2個)或(英文字母+英文字母AJQK),
用正則表達式可以很方便的搜索這種格式,
我的程式如下:

import re
def sort_poker(john, uncle):
    txt = re.findall(r'([A-Z])(\d+|[AJQK])', john)
    txt = list(map(''.join, txt))
    values= {'2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9, '10': 10, 'J': 11, 'Q': 12, 'K': 13, 'A': 14}
    return ''.join(sorted(txt, key= lambda c: (uncle.index(c[0]), values[c[1:]])))

1 則留言

1
twelve8462
iT邦新手 5 級 ‧ 2020-06-13 16:29:57

感謝小馬哥的系列文

小弟從最初級開始觀看,到黑魔法為止受益良多

今天的練習有點困難阿
加上英文網站有點搞不太懂她題目的意思(英文太poor...
後來才搞懂原來是john的手牌排序必須按照叔叔的排法

小馬哥的解答讓我學習到
\d+是1or多個數字
10特別的傷腦筋,只有他是2位數,真的一開始都想不出來有[1:]這樣的解法XD

心原一馬 iT邦研究生 5 級 ‧ 2020-06-13 16:42:05 檢舉

嗨嗨~ 謝謝你的分享,
很高興小馬的python文章對你的自學有幫助。
其實我也覺得re正則表達式蠻不好懂的,
反覆閱讀很多次才大概學會,
祝福你學習順利~
/images/emoticon/emoticon37.gif

我要留言

立即登入留言