Python 正規表達式教學:看懂 re.split()、\W|_ 與 flags=re.ASCII # \w代表 word character ~ [A-Za-z0-9_]

加入好友
加入社群
Python 正規表達式教學:看懂 re.split()、W|_ 與 flags=re.ASCII # w代表 word character ~ [A-Za-z0-9_] - 儲蓄保險王

這篇會用 Python re 模組 來講清楚三個很常一起出現、卻也很容易混淆的東西:

  • re.split()
  • \W|_
  • flags=re.ASCII

也會順便說明:

  • \w / \W 到底是什麼
  • 為什麼中文會影響匹配結果
  • 什麼時候用 re.split() 反而比複雜 regex 更清楚

一、先認識 re.split()

re.split() 的用途是:

用「符合 regex 的分隔符」來切字串

一般字串切割你可能會寫:

text.split("_")

但如果你的分隔符不只一種,例如:

  • _
  • -
  • 空白

str.split() 就不夠方便了,這時可以用 re.split()


基本語法

import re

re.split(pattern, text)
  • pattern:分隔規則(regex)
  • text:要切的字串

例子:同時用底線、連字號、空白切字串

import re

text = "ARGOS_V3-Rayado Peak"
parts = re.split(r'[_\-\s]+', text)
print(parts)

輸出:

['ARGOS', 'V3', 'Rayado', 'Peak']

這個 pattern 是什麼意思?

r'[_\-\s]+'

代表:

  • _:底線
  • \-:連字號 -
  • \s:空白字元
  • +:一個以上連續出現都當成同一個分隔段

所以像:

ARGOS__V3   Rayado-Peak

也能正確切開。

Python 正規表達式教學:看懂 re.split()、W|_ 與 flags=re.ASCII # w代表 word character ~ [A-Za-z0-9_] - 儲蓄保險王

二、為什麼 re.split() 很實用?

因為很多時候你真正要做的不是:

「寫一條超複雜 regex 一次抓完所有 token」

而是:

「先把明顯分隔符切掉,再對每段做進一步分析」

這種做法通常有三個優點:

1. 可讀性高

你一看就知道分兩步:

  • 第一步:切外層分隔符
  • 第二步:處理每個片段

2. 好維護

之後如果你想加 ./,只要改 split pattern。

3. 規則更單純

避免把所有條件都塞進 lookahead / lookbehind。


三、什麼是 \w\W

這兩個是 regex 常見 shorthand。

\w

代表 word character

\W

代表 non-word character
也就是「不是 \w 的字元」


在很多教學裡,\w 常被簡化理解成:

[A-Za-z0-9_]

也就是:

  • 英文大小寫
  • 數字
  • 底線 _

因此:\W

就可以理解成:

[^A-Za-z0-9_]

範例

import re

text = "ABC_123-xy"

print(re.findall(r'\w+', text))
print(re.findall(r'\W+', text))

輸出大致是:

Python 正規表達式教學:看懂 re.split()、W|_ 與 flags=re.ASCII # w代表 word character ~ [A-Za-z0-9_] - 儲蓄保險王

原因是:

  • ABC_123 都是 \w
  • -\W

四、那為什麼有時候會看到 \W|_

這個寫法的意思是:

「非單字字元,或底線 _

\W|_

你可能會想問:

_ 不是本來就算在 \W 裡嗎?

答案是:

不是

因為 _ 屬於 \w,不是 \W

所以:

  • \W 抓不到 _
  • 如果你想把 _ 也當分隔條件,就要另外寫 _

這就是為什麼會看到:

\W|_

範例

import re

text = "ABC_123-xy"

print(re.split(r'\W+', text))
print(re.split(r'\W|_', text))

輸出是:

Python 正規表達式教學:看懂 re.split()、W|_ 與 flags=re.ASCII # w代表 word character ~ [A-Za-z0-9_] - 儲蓄保險王

解讀

r'\W+'

只會用非 word char 切
所以:

  • - 會切
  • _ 不會切

r'\W|_'

會用:

  • 所有 \W
  • 以及 _

來切,所以 _ 也會成為分隔符。


五、re.split()\W|_ 有什麼關係?

如果你寫:

re.split(r'\W|_', text)

意思是:

用「非單字字元 或 底線」來切字串

這是一種很常見的切法。

例如:

import re

text = "ARGOS_V3-Test"
print(re.split(r'\W|_', text))

輸出:

Python 正規表達式教學:看懂 re.split()、W|_ 與 flags=re.ASCII # w代表 word character ~ [A-Za-z0-9_] - 儲蓄保險王

這和你手動列出分隔符很像。


但兩者思路不同

寫法 A:顯式列出分隔符

re.split(r'[_\-\s]+', text)

寫法 B:使用字元類別

re.split(r'\W|_', text)

差異在哪?

A 的特點

你明確指定:

  • _
  • -
  • 空白

更可控、更直觀。

B 的特點

你把很多符號都交給 \W 處理,例如:

  • -
  • .
  • /
  • 空白
  • (
  • )
  • ,

寫起來短,但抽象程度較高。


六、flags=re.ASCII 是什麼?

這是很多人第一次碰到會忽略、但實際很重要的設定。

flags=re.ASCII

會讓某些 regex shorthand 按照 ASCII 規則 來判定,而不是 Unicode 規則。

特別重要的是:

  • \w
  • \W
  • \b
  • \B

七、沒有 re.ASCII 時,中文會怎樣?

在 Python 3 預設情況下,regex 是 Unicode-aware 的。

這表示:

\w 不只包含英文數字底線,通常也包含中文等 Unicode 字元

例如:

import re

text = "ABC中文_123"

print(re.findall(r'\w+', text))

輸出是:

Python 正規表達式教學:看懂 re.split()、W|_ 與 flags=re.ASCII # w代表 word character ~ [A-Za-z0-9_] - 儲蓄保險王

這代表:

  • A B C\w
  • 中 文 也是 \w
  • _123 也是 \w

所以整段被視為同一個 word token。


八、加上 flags=re.ASCII 會怎樣?

import re

text = "ABC中文_123"

print(re.findall(r'\w+', text, flags=re.ASCII))

輸出是:

Python 正規表達式教學:看懂 re.split()、W|_ 與 flags=re.ASCII # w代表 word character ~ [A-Za-z0-9_] - 儲蓄保險王

因為在 ASCII 模式下:\w

比較接近:

[A-Za-z0-9_]

中文不再被算進 \w


九、所以 re.ASCII 可以怎麼理解?

可以用一句話理解:

\w\W 這類 shorthand 不把中文當成 word character

但更精確地說:

  • 它不是「禁止中文出現」
  • 而是讓 regex 的某些類別判定改用 ASCII 規則

所以像這樣的 pattern:

r'中文'

即使用了 re.ASCII,還是能匹配中文。


十、re.split(r'\W|_', ...) 遇到中文時的差異

這是最值得注意的地方。


例子一:預設模式

# %%
import re

text = "ABC中文_123-xy"
print(re.split(r'\W|_', text))

結果是:

Python 正規表達式教學:看懂 re.split()、W|_ 與 flags=re.ASCII # w代表 word character ~ [A-Za-z0-9_] - 儲蓄保險王

因為:

  • 中文在預設下常被視為 \w
  • 所以 \W 不會拿中文當分隔符
  • _ 會切
  • - 會切

例子二:ASCII 模式

import re

text = "ABC中文_123-xy"
print(re.split(r'\W|_', text, flags=re.ASCII))

結果是:

Python 正規表達式教學:看懂 re.split()、W|_ 與 flags=re.ASCII # w代表 word character ~ [A-Za-z0-9_] - 儲蓄保險王

這時中文會被視為 \W,因此成為分隔區的一部分。


十一、為什麼會出現空字串 ''

因為 re.split() 如果分隔符連續出現,或字串開頭/結尾剛好命中分隔條件,就可能產生空字串。

例如:

import re

text = "ABC中文_123"
print(re.split(r'\W|_', text, flags=re.ASCII))

中文和 _ 連續造成切割,就可能出現空項。

Python 正規表達式教學:看懂 re.split()、W|_ 與 flags=re.ASCII # w代表 word character ~ [A-Za-z0-9_] - 儲蓄保險王

這時常見做法是過濾掉:

parts = [p for p in re.split(r'\W|_', text, flags=re.ASCII) if p]

十二、什麼時候用 re.split(r'\W|_', ...)

適合在你想快速做這種處理時:

  • 非字母數字底線的符號都當分隔符
  • _ 也要額外算分隔符

例如很多 identifier normalization、tokenization 的前處理。


十三、什麼時候直接列出分隔符比較好?

如果你的資料格式很明確,例如你只想切:

  • _
  • -
  • 空白

那這樣通常更好懂:

re.split(r'[_\-\s]+', text)

因為它一眼就看得出規則。

這在處理:

  • class names
  • family names
  • file naming conventions
  • metadata 欄位

時尤其實用。


十四、兩種寫法怎麼選?

我建議這樣記:


用顯式 split:

re.split(r'[_\-\s]+', text)

適合:

  • 你知道要切哪些符號
  • 規則固定
  • 希望程式可讀性高

\W|_

re.split(r'\W|_', text)

適合:

  • 你想把大部分非 word char 都當分隔符
  • 不想一個一個列舉符號
  • 你清楚 \w/\W 會受 Unicode / ASCII 影響

十五、實務上最容易踩的坑

坑 1:以為 \W 包含 _

其實不包含,所以才會常看到:

\W|_

坑 2:以為 \W 一定會把中文切掉

不一定。
在 Python 3 預設 regex 下,中文常常屬於 \w,所以不是 \W


坑 3:忘記 re.ASCII 會改變 \w/\W 行為

同一個 regex,加不加 flags=re.ASCII,結果可能差很多。


十六、推薦的理解方式

把這三者拆開理解最清楚:

re.split()

是工具:用 regex 當分隔符切字串

\W|_

是分隔規則:非 word char 或底線

flags=re.ASCII

是匹配模式:\w/\W 改用 ASCII 判定


十七、完整示範

import re

text = "ARGOS中文_V3-Test"

print("1. 顯式列分隔符")
print(re.split(r'[_\-\s]+', text))

print("2. 用 \\W|_,預設模式")
print(re.split(r'\W|_', text))

print("3. 用 \\W|_,ASCII 模式")
print(re.split(r'\W|_', text, flags=re.ASCII))

輸出:

Python 正規表達式教學:看懂 re.split()、W|_ 與 flags=re.ASCII # w代表 word character ~ [A-Za-z0-9_] - 儲蓄保險王

十八、這個例子說明了什麼?

顯式列分隔符

只切你指定的 _ - 空白

\W|_ 預設模式

中文不是 \W,所以效果看起來和上面很像

\W|_ + re.ASCII

中文變成非 \w,因此會被切開


十九、如果只是做英文命名切詞,我的建議是什麼?

如果你的資料本來就是偏英文命名規則,例如:

  • ARGOS_V3
  • RayadoPeak
  • XMLParser

那我通常會建議:

先用 re.split() 顯式切外層分隔符

re.split(r'[_\-\s]+', text)

再對每段做 CamelCase 拆詞

這通常比直接依賴 \W|_ 更清楚、可控。


二十、總結

re.split()

用 regex 當分隔符切字串,適合先處理外層分隔符。

\W

表示非 word character,但 _ 不包含在內。

\W|_

代表「非 word character 或底線 _」,常用來做通用切詞。

flags=re.ASCII

會讓 \w/\W 等 shorthand 改採 ASCII 規則,中文不再被視為 \w

實務建議

如果你知道要切哪些符號,顯式使用 re.split(r'[_\-\s]+', ...) 通常更直觀、更易維護。

推薦hahow線上學習python: https://igrape.net/30afN

二十一、補充:為什麼很多人會想談 Unicode?

前面我們提到:

  • \W|_
  • re.split()
  • flags=re.ASCII

這些主題一旦談到「中文為什麼沒被切開」、「為什麼加了 re.ASCII 結果不同」,就幾乎一定會碰到 Unicode

也就是說,如果你看不懂 Unicode 在 regex 裡扮演什麼角色,就很容易搞不清楚 \w\W 的行為差異。


二十二、Unicode 是什麼?

你可以先把 Unicode 理解成:

一套幫全世界文字編號的標準

英文、數字、中文、日文、韓文、emoji……
這些字元在電腦裡都需要有對應的編碼位置,而 Unicode 就是在做這件事。

例如:

  • A
  • 😊

它們在 Unicode 裡都有各自的 code point。


二十三、為什麼 regex 會跟 Unicode 有關?

因為 regex 裡面像這種 shorthand:

  • \w
  • \W
  • \b
  • \d
  • \s

看起來很簡短,但它們到底「算哪些字元」,其實跟匹配模式有關。

在 Python 3 裡,regex 預設是走 Unicode-aware 的路線,這表示:

很多非 ASCII 字元,也可能被視為某種合法字元類別

所以你才會看到:

  • 中文有時會被算進 \w
  • 不加 re.ASCII 時,\W 不一定會把中文當分隔符

二十四、先用一個觀念記住就好

預設模式

Python 3 的 re 會比較傾向用 Unicode 規則 來理解字元。

re.ASCII 模式

Python 3 的 re 會把某些 shorthand 改成只看 ASCII 範圍。

這就是前面結果不同的根本原因。


二十五、什麼叫 Unicode 範圍?

如果你在 regex 裡看到這種寫法:

[\u4e00-\u9fff]

這就是在寫 Unicode 範圍

意思是:

匹配 Unicode code point 從 \u4e00\u9fff 之間的字元

這一段常被拿來匹配「常見中文漢字區」。


二十六、最常見的中文範圍例子

r'[\u4e00-\u9fff]+'

這通常會用來抓一段連續中文,例如:

import re

text = "ABC中文DEF"
print(re.findall(r'[\u4e00-\u9fff]+', text))
Python 正規表達式教學:看懂 re.split()、W|_ 與 flags=re.ASCII # w代表 word character ~ [A-Za-z0-9_] - 儲蓄保險王

這表示 regex 正在明確指定:

  • 我要的是某段 Unicode 區間
  • 不是靠 \w
  • 不是靠 \W
  • 而是直接指定「字元碼位範圍」

二十七、Unicode 範圍和 \w / \W 有什麼不同?

差別非常大。

\w

是「類別概念」
意思比較像:

這個字元在目前規則下算不算 word character?

[\u4e00-\u9fff]

是「明確列範圍」
意思比較像:

我只接受這一段 code point 內的字元


二十八、哪一種比較精準?

如果你的目標是:

我就是要抓某一段特定語系字元

那 Unicode 範圍通常更精準。

例如:

r'[\u4e00-\u9fff]+'

它不依賴 \w 的定義,也不依賴 re.ASCII 是否開啟。
它就是直接說:我要這段 Unicode 區間內的字元。


二十九、但為什麼很多人還是覺得 Unicode 範圍難用?

因為它通常也是:

最難記、最不直覺的一種寫法

例如:

[\u4e00-\u9fff]

你很難一眼看出來它代表什麼,除非你本來就熟。

而且還會產生很多疑問:

  • 這段是不是全部中文?
  • 有沒有漏掉延伸漢字?
  • 標點算不算?
  • 全形符號算不算?
  • 簡體、繁體都包含嗎?

所以雖然它「精準」,但不一定「好用」。


三十、這也是為什麼 Unicode 範圍常被認為最難記

像:

  • \u4e00-\u9fff
  • \u3400-\u4dbf

這些區間沒有太多語意線索,很少有人能自然背下來。

實務上常常是:

  • 查資料
  • 複製貼上
  • 隔一陣子又忘

所以如果你只是要處理英文命名、token split、identifier normalization,
通常不會優先想用 Unicode 範圍。


三十一、Unicode 範圍的優點

雖然難記,但它有幾個明確優點。

1. 匹配目標明確

你指定哪個區間,就只抓哪個區間。

2. 不受 \w/\W 定義影響

不會因為有沒有加 re.ASCII 而改變意思。

3. 適合語系限制很明確的需求

例如你真的要檢查:

  • 是否含中文
  • 是否只允許中文與英文數字
  • 是否抽取中文片段

三十二、Unicode 範圍的缺點

1. 不好記

這是最大問題。

2. 可讀性差

新手看到幾乎無法立刻理解。

3. 容易誤以為「這樣就涵蓋全部中文」

其實很多情況下,你抓的只是常用區,不一定真的涵蓋所有相關字元。

4. 維護成本高

未來別人接手程式時,不一定看得懂你的意圖。


三十三、簡單範例:抓出中文字串

import re

text = "Python正規表達式123"
chs = re.findall(r'[\u4e00-\u9fff]+', text)
print(chs)
Python 正規表達式教學:看懂 re.split()、W|_ 與 flags=re.ASCII # w代表 word character ~ [A-Za-z0-9_] - 儲蓄保險王

這種寫法的意思很單純:

從字串裡找出連續的常見中文漢字區段


三十四、簡單範例:判斷字串是否包含中文

import re

text = "ARGOS中文_V3"

if re.search(r'[\u4e00-\u9fff]', text):
    print("包含中文")
else:
    print("不包含中文")
Python 正規表達式教學:看懂 re.split()、W|_ 與 flags=re.ASCII # w代表 word character ~ [A-Za-z0-9_] - 儲蓄保險王

三十五、簡單範例:只保留英文、數字、常見中文

import re

text = "ARGOS中文-V3!"
cleaned = re.sub(r'[^A-Za-z0-9\u4e00-\u9fff]+', '', text)
print(cleaned)
Python 正規表達式教學:看懂 re.split()、W|_ 與 flags=re.ASCII # w代表 word character ~ [A-Za-z0-9_] - 儲蓄保險王

這種寫法代表:

  • 保留英文字母
  • 保留數字
  • 保留常見中文
  • 其他符號移除

三十六、這和 re.ASCII 的思路有什麼不同?

差別可以這樣理解:

re.ASCII

是在改變 shorthand 的判定規則
例如:

  • \w
  • \W

Unicode 範圍

是在直接指定你要哪些字元


三十七、一個對照例子

import re

text = "ABC中文_123"

寫法一:看 \w

print(re.findall(r'\w+', text))
print(re.findall(r'\w+', text, flags=re.ASCII))
Python 正規表達式教學:看懂 re.split()、W|_ 與 flags=re.ASCII # w代表 word character ~ [A-Za-z0-9_] - 儲蓄保險王

這表示 \w 的定義會因 re.ASCII 改變。


寫法二:直接抓中文範圍

print(re.findall(r'[\u4e00-\u9fff]+', text))
Python 正規表達式教學:看懂 re.split()、W|_ 與 flags=re.ASCII # w代表 word character ~ [A-Za-z0-9_] - 儲蓄保險王

這不依賴 re.ASCII,因為你已經明確寫死要抓哪段 Unicode 區間。


三十八、那到底該用哪一種?

可以用這個原則判斷。

如果你想處理的是:

  • token 邊界
  • 英文命名切詞
  • _-、空白這類分隔符
  • \w/\W 的 ASCII 行為

那通常優先考慮:

  • re.split()
  • \W|_
  • flags=re.ASCII

如果你想處理的是:

  • 是否含中文
  • 抽取中文片段
  • 只允許特定語系字元
  • 明確限制字元集合

那 Unicode 範圍就比較合適。


三十九、實務建議:先選「語意最清楚」的寫法

這很重要。

不要因為 Unicode 範圍「看起來很強」就優先使用它。
很多時候,真正好的寫法不是最炫,而是 最能表達意圖

例如:

你只是想把 _ - 空白 切開

那就寫:

re.split(r'[_\-\s]+', text)

你想把非 word char 和 _ 都視為分隔符

那就寫:

re.split(r'\W|_', text, flags=re.ASCII)

你真的想抓中文

那才寫:

re.findall(r'[\u4e00-\u9fff]+', text)

四十、把 Unicode 這段和前文串起來理解

現在可以把前面的觀念整合成三層:

第一層:工具

re.split()
re.findall()
re.search()
re.sub()

第二層:規則

\W|_
[_\-\s]+
[\u4e00-\u9fff]+

第三層:匹配模式

flags=re.ASCII

這樣你就比較不會把它們混在一起。


四十一、這一段最值得記住的重點

1.

Unicode 範圍是直接指定字元區間,不是 shorthand 類別。

2.

它通常比 re.ASCII 更精準,但也更難記、更難維護。

3.

如果只是做一般英文 token split,通常沒必要一開始就使用 Unicode 範圍。

4.

只有在你真的要控制「某一類 Unicode 字元」時,Unicode 範圍才特別有價值。


四十二、補充結論

如果要用一句話總結這段 Unicode 教學,可以這樣說:

re.ASCII 是在改變 \w/\W 這類 shorthand 的判定方式;
Unicode 範圍則是直接指定你要匹配哪些字元。前者比較好懂,後者比較精準,但通常也更難記、更難用。

推薦hahow線上學習python: https://igrape.net/30afN

這裡的 ASCII 規則,意思是:

在判斷 \w\W\d\s\b 這類 regex shorthand 時,
只用 ASCII 字元集合來判定,不去採用較寬鬆的 Unicode 字元分類。


四十三、什麼叫做 ASCII 規則?

前面說 flags=re.ASCII 會讓某些 regex shorthand 按照 ASCII 規則 判定,這裡的 ASCII 規則,可以先理解成:

只把 ASCII 範圍內的字元當成判斷依據

ASCII 是一套比較早期、範圍比較小的字元編碼集合,常見可理解為:

  • 英文大小寫字母:A-Za-z
  • 數字:0-9
  • 常見符號
  • 空白字元的一部分

重點是:

它不包含中文、日文、韓文、emoji 這些 Unicode 字元

所以當 regex 使用 re.ASCII 時,某些 shorthand 就不會把這些非 ASCII 字元算進去。


四十四、在 regex 裡,ASCII 規則最常影響哪些 shorthand?

最常見的是這幾個:

  • \w
  • \W
  • \d
  • \D
  • \s
  • \S
  • \b
  • \B

其中在文章脈絡裡,最重要的是:

  • \w
  • \W
  • \b

四十五、\\w 在 ASCII 規則下代表什麼?

如果加上:

flags=re.ASCII

那麼:\w

就大致等於:

[A-Za-z0-9_]

也就是只把下面這些當作 word character:

  • 英文字母
  • 數字
  • 底線 _

所以:

  • A\w
  • 7\w
  • _\w
  • 不是 \w
  • 不是 \w
  • é 在這個規則下也不算 \w

四十六、\\W 在 ASCII 規則下代表什麼?

\W 就是 \w 的相反。

如果 ASCII 模式下:

\w == [A-Za-z0-9_]

那麼:\W

就可以理解成:

[^A-Za-z0-9_]

所以:

  • -\W
  • 空白是 \W
  • \W
  • @\W

但要注意:

  • _ 不是 \W
  • 因為 _ 屬於 \w

這也是為什麼常常會看到:

\W|_

因為單靠 \W 抓不到 _


四十七、ASCII 規則和 Unicode 規則最大的差別是什麼?

最大差別在於:

非英文字符要不要被算進 shorthand 類別

例如字串:

text = "ABC中文_123"

不加 re.ASCII

re.findall(r'\w+', text)
# 輸出: ['ABC中文_123']

加入好友
加入社群
Python 正規表達式教學:看懂 re.split()、W|_ 與 flags=re.ASCII # w代表 word character ~ [A-Za-z0-9_] - 儲蓄保險王

儲蓄保險王

儲蓄險是板主最喜愛的儲蓄工具,最喜愛的投資理財工具則是ETF,最喜愛的省錢工具則是信用卡

You may also like...

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *