用正規表示式整理地方故事、電話與感測器編號
用可讀的文字規則,從逐字稿與巡查紀錄中搜尋、驗證、擷取及遮罩資料。
完成本篇後,你能讀懂常用正規表示式符號,使用
fullmatch()、search()、findall()、finditer()、split() 與 sub(),並以群組整理感測器編號、日期及逐字稿。一、什麼時候需要正規表示式?
一般字串方法適合搜尋固定文字;當資料具有「形式固定、內容會變」的特徵,例如 TEMP-01、PH-07 或日期,就可使用正規表示式描述格式。
二、先認識 re 與原始字串
import re
text = "今日巡查 TEMP-01 與 PH-07。"
match = re.search(r"TEMP-\d{2}", text)
if match:
print("找到:", match.group())
print("位置:", match.span())模式前的 r 表示原始字串,可減少反斜線被 Python 字串先行解讀的問題。正規表示式通常建議寫成 r"..."。
三、常用符號速查
| 符號 | 意義 | 例子 |
|---|---|---|
\d | 數字 | \d{2} 兩位數字 |
\s | 空白字元 | \s+ 一個以上空白 |
. | 除換行外的任意字元 | 需小心範圍過大 |
[ABC] | 指定集合中的一個字元 | [A-Z] 大寫英文字母 |
* + ? | 0次以上、1次以上、0或1次 | -? 可有可無的連字號 |
{m,n} | 重複次數 | \d{2,3} |
^ $ | 字串開頭、結尾 | 完整格式限制 |
() | 群組及擷取 | (TEMP|PH) |
四、fullmatch:驗證完整感測器編號
import re
pattern = re.compile(r"(TEMP|PH|DO)-\d{2}")
sensor_ids = ["TEMP-01", "PH-07", "DO-12", "TEMP-1", "XPH-07"]
for sensor_id in sensor_ids:
valid = pattern.fullmatch(sensor_id) is not None
print(sensor_id, "格式正確" if valid else "格式錯誤")fullmatch() 要求整個字串符合模式,適合欄位格式驗證;search() 只要在文字任一位置找到即可。
五、群組:把編號拆成種類與序號
import re
pattern = re.compile(
r"(?P<kind>TEMP|PH|DO)-(?P<number>\d{2})"
)
match = pattern.fullmatch("TEMP-03")
if match:
print("種類:", match.group("kind"))
print("序號:", match.group("number"))
print(match.groupdict())命名群組 (?P<名稱>...) 比只用第 1、2 群組更容易閱讀,適合後續轉成字典或資料庫欄位。
六、findall 與 finditer:找出全部符合項目
import re
log = "TEMP-01 正常;PH-07 待確認;TEMP-02 正常。"
pattern = re.compile(r"(?:TEMP|PH|DO)-\d{2}")
print(pattern.findall(log))
for match in pattern.finditer(log):
print(match.group(), match.start(), match.end())findall() 快速取得結果;需要位置或群組細節時,使用 finditer()。模式中的 (?:...) 是不擷取群組,可避免 findall() 只回傳群組內容。
七、擷取巡查日期
import re
from datetime import date
pattern = re.compile(
r"(?P<year>\d{4})[-/]"
r"(?P<month>\d{1,2})[-/]"
r"(?P<day>\d{1,2})"
)
text = "巡查日期:2026/08/30"
match = pattern.search(text)
if match:
try:
patrol_date = date(
int(match.group("year")),
int(match.group("month")),
int(match.group("day")),
)
except ValueError as error:
print("日期不存在:", error)
else:
print(patrol_date)正規表示式只能確認外觀。像 2026/02/31 也可能符合模式,所以還要交給 date 檢查日期是否存在。
八、整理逐字稿空白與重複標點
import re
import unicodedata
def clean_transcript(text):
text = unicodedata.normalize("NFC", text)
text = re.sub(r"[ \t]+", " ", text)
text = re.sub(r"。{2,}", "。", text)
text = re.sub(r"\n{3,}", "\n\n", text)
return text.strip()
raw = "阿嬤講: 姻緣花開矣。。。\n\n\n這是庄頭的記持。"
print(clean_transcript(raw))九、用 split 切分多種標點
import re
text = "白馬、烏龜;姻緣花,柴井車"
items = re.split(r"[、;,]\s*", text)
for item in items:
print(item)方括號表示其中任一字元。若分隔規則只是單一固定字串,優先使用一般的 str.split(),會更簡單易懂。
十、電話遮罩:公開前降低識別風險
以下僅示範常見手機形式的遮罩,不宣稱涵蓋所有合法電話格式。
import re
def mask_mobile(text):
pattern = re.compile(r"(?<!\d)(09\d{2})-?(\d{3})-?(\d{3})(?!\d)")
return pattern.sub(r"\1-***-\3", text)
note = "聯絡電話 0912-345-678,僅供教學示範。"
print(mask_mobile(note))遮罩不是匿名化保證;姓名、地址、職稱與事件內容交叉比對後仍可能辨識個人。公開社區資料前應取得同意並做整體風險檢查。
十一、批次解析巡查紀錄
import re
pattern = re.compile(
r"^(?P<sensor>(?:TEMP|PH|DO)-\d{2})"
r"\s*,\s*(?P<value>-?\d+(?:\.\d+)?)"
r"\s*,\s*(?P<unit>[^,]+)$"
)
lines = [
"TEMP-01, 27.2, °C",
"PH-07, 7.4, pH",
"TEMP-2, 離線, °C",
]
records = []
errors = []
for line_number, line in enumerate(lines, start=1):
match = pattern.fullmatch(line.strip())
if match is None:
errors.append(f"第 {line_number} 行格式錯誤:{line}")
continue
record = match.groupdict()
record["value"] = float(record["value"])
records.append(record)
print(records)
print(errors)十二、貪婪與非貪婪
import re
text = "【白馬】故事一【烏龜】故事二"
greedy = re.findall(r"【.*】", text)
non_greedy = re.findall(r"【.*?】", text)
print("貪婪:", greedy)
print("非貪婪:", non_greedy)* 預設會盡量多吃字元,*? 則盡量少。更穩定的寫法通常是明確排除結束符號,例如標題可寫成 r"【[^】]+】"。
十三、常見錯誤檢查表
| 現象 | 原因 | 修正 |
|---|---|---|
| 明明格式多字卻驗證成功 | 使用 search 而非 fullmatch | 完整欄位改用 fullmatch |
| 反斜線規則失效 | 忘記原始字串 | 模式使用 r"..." |
| findall 只回傳部分內容 | 模式含擷取群組 | 改用非擷取群組或 finditer |
| 一次抓到兩個標題 | 量詞太貪婪 | 使用非貪婪或排除字元 |
| 模式可讀性很差 | 一條正規式承擔太多工作 | 拆成步驟並用 Python 驗證語意 |
十四、USR 實作挑戰
為教學用水溫、pH、溶氧設備定義編號規格,找出重複、缺號與格式錯誤項目。
在已授權逐字稿中搜尋「白馬、烏龜、姻緣花」出現位置,建立人工複核清單,不自動改寫內容。
以規則找出可能的手機號碼並遮罩,再由人工檢查姓名、住址與情境交叉識別風險。
十五、與生成式 AI 協作
請擔任 Python 正規表示式助教。
請為我的地方故事與感測紀錄提出正規表示式:
1. 先用自然語言說明格式;
2. 模式使用 Python 原始字串;
3. 完整欄位使用 fullmatch;
4. 提供至少五個應成功與五個應失敗案例;
5. 日期須另用 datetime 驗證真實性;
6. 個資遮罩後仍提醒人工檢查;
7. 不得擅自刪除台語逐字稿內容。
請說明每個群組與量詞的作用。正規表示式擅長辨識「格式」,但不負責判斷「真實、合理與已授權」。把模式保持精簡,搭配一般 Python 驗證與人工確認,才能可靠整理場域資料。
沒有留言:
張貼留言