Regular expressions describe patterns in text. In data cleaning they are useful for three distinct jobs: validating a shape, extracting components, and transforming a known representation. Problems begin when a shape match is treated as proof of meaning.
Read a pattern in layers
For an IPv4-like shape:
^(\d{1,3}\.){3}\d{1,3}$
^and$anchor the whole value;\d{1,3}matches one to three digits;\.matches a literal dot;{3}repeats the grouped prefix three times.
It accepts 999.999.999.999, so it validates syntax but not the numeric domain. Parse the four components and enforce 0..255 separately.
In plain terms: regex can recognize the shape of an address on an envelope. It cannot prove the address exists.
Match, extract, and replace are different operations
import re
raw = "2026/9/7"
m = re.fullmatch(r"(\d{4})/(\d{1,2})/(\d{1,2})", raw)
if m:
year, month, day = map(int, m.groups())
After extraction, use a date library to validate month lengths and leap years. Only then emit an ISO value such as 2026-09-07.
A replacement should target what was captured:
re.sub(r"^([^,]+),\s*([^,]+)$", r"\2 \1", "Lin, Johnny")
This transformation is safe only if the source convention is known. Names are not universally two comma-separated tokens.
Avoid the common traps
| Trap | Symptom | Better approach |
|---|---|---|
| Missing anchors | Valid substring inside invalid text passes | Use full match for validation |
| Greedy wildcard | One group consumes too much | Use explicit delimiters or non-greedy match |
| Catastrophic backtracking | Runtime spikes on crafted input | Simplify nested repetition and bound length |
| One giant pattern | Impossible to review or explain | Parse in stages with named groups |
| Engine assumptions | Pattern works in one tool only | Record flavor and flags |
Unicode also matters. \w, case folding, and character classes behave differently across engines and flags. Test accents, non-Latin scripts, and composed versus decomposed characters when the dataset contains them.
Build a test matrix before mass editing
| Case | Input | Expected |
|---|---|---|
| Normal | john@example.com |
accept |
| Boundary | one-character local part | accept if policy allows |
| Malformed | missing domain | reject |
| Near miss | trailing comment or space | normalize or reject explicitly |
| Adversarial | very long repeated text | finish quickly |
Store the examples beside the cleaning rule. A regex without positive and negative fixtures is an undocumented guess.
Know when to stop using regex
Use a parser for nested formats, CSV quoting, JSON, URLs, dates, and programming-language syntax. Use reference data or business rules for identity and geography. Regex remains valuable at the boundary: extracting a candidate or rejecting an impossible shape before stronger validation.
Review card
- Anchor validation patterns to the whole value.
- Separate syntactic matching from domain and semantic validation.
- Extract components first; canonicalize only after validation.
- Record regex flavor, flags, and Unicode assumptions.
- Test valid, invalid, boundary, near-miss, and adversarial inputs.
- Prefer a real parser when the format has nested grammar.
Regular expression 描述 text pattern。在 data cleaning 裡主要做三件事:驗證 shape、抽取 component、轉換已知 representation。問題通常發生在把「形狀 match」誤當成「語意正確」。
分層讀 pattern
IPv4-like shape:
^(\d{1,3}\.){3}\d{1,3}$
^、$anchor 整個 value;\d{1,3}匹配 1–3 個 digit;\.匹配 literal dot;{3}重複 grouped prefix 三次。
它也接受 999.999.999.999,所以只驗證 syntax,沒有驗 numeric domain。四段 parse 完後,仍要分別檢查 0..255。
白話來說: Regex 能認出信封上地址的形狀,不能證明那個地址真的存在。
Match、extract、replace 是不同 operation
import re
raw = "2026/9/7"
m = re.fullmatch(r"(\d{4})/(\d{1,2})/(\d{1,2})", raw)
if m:
year, month, day = map(int, m.groups())
Extraction 之後要用 date library 驗證月份天數和 leap year,確認後才能輸出 2026-09-07。
Replacement 也要只處理 captured component:
re.sub(r"^([^,]+),\s*([^,]+)$", r"\2 \1", "Lin, Johnny")
只有 source convention 明確時這才安全;人名並不普遍等於兩個 comma-separated token。
常見陷阱
| 陷阱 | 症狀 | 改法 |
|---|---|---|
| 沒有 anchor | Invalid text 裡一小段 valid 就通過 | Validation 使用 full match |
| Greedy wildcard | Group 吃掉太多內容 | 明確 delimiter 或 non-greedy |
| Catastrophic backtracking | 特定 input 令 runtime 暴增 | 簡化 nested repetition、限制長度 |
| 一個超大 pattern | 無法 review 與解釋 | 分階段 parse、使用 named group |
| 假設 engine 相同 | 換工具後行為不同 | 記錄 flavor 與 flag |
Unicode 也會影響 \w、case folding 與 character class。Dataset 有 accent、非拉丁文字或 composed/decomposed form 時,都要加 fixture。
Mass edit 前先建 test matrix
| Case | Input | Expected |
|---|---|---|
| Normal | john@example.com |
accept |
| Boundary | 一字元 local part | policy 允許則 accept |
| Malformed | 缺 domain | reject |
| Near miss | 尾端空白或 comment | 明確 normalize 或 reject |
| Adversarial | 很長的重複 text | 必須快速結束 |
把例子和 cleaning rule 放在一起。沒有 positive/negative fixture 的 regex,只是沒文件的猜測。
何時不要再用 regex
Nested format、CSV quoting、JSON、URL、date 與 programming-language syntax 應交給 parser;identity、geography 要用 reference data 或 business rule。Regex 適合留在 boundary:先抽 candidate,或在進入強驗證前排除不可能的 shape。
複習卡
- Validation pattern 要 anchor 全值。
- Syntax match 與 domain/semantic validation 分開。
- 先 extract component,驗證後才 canonicalize。
- 記錄 regex flavor、flag 與 Unicode assumption。
- 測 valid、invalid、boundary、near-miss、adversarial input。
- Format 有 nested grammar 時,優先用真正 parser。