若要在 Java 中從字串移除標點符號,請呼叫 String.replaceAll("\\p{P}+", "") — \p{P} 這個 Unicode 屬性逸出會比對七個 Unicode 標點子分類(Pc、Pd、Ps、Pe、Po、Pi 和 Pf)中的每一個字元,而 replaceAll 會在單一線性掃描中將它們全部刪除。Java 開發人員會在清理日誌行、為自然語言處理進行文字斷詞、在儲存前正規化使用者輸入,或為全文索引準備字串時選用這個模式。java.util.regex 內建的 regex 引擎預設具有 Unicode 感知能力,這代表 \p{P} 能做到大多數手刻 [^a-zA-Z0-9 ] 模式做不到的事 — 它確實會移除真正的標點,而非憑猜測處理 ASCII。不過,純 regex 有已知的弱點:縮寫會塌縮、連字複合詞會失去連字號、小數點會從價格中消失,引號會被當成撇號處理。本指南先介紹 Java 程式碼,接著說明 移除標點 工具如何補抓 regex 留下的 bug。

七個 Unicode 標點子分類
Unicode 將每個碼位分組到通用分類中,標點歸於分類 P,並細分為七個子分類。Pc 是連接符標點(底線及特定連接記號),Pd 是破折號(連字號、en dash 和 em dash),Ps 是起始標點如 ( [ {,Pe 是結束標點如 ) ] },Po 是「其他」標點,包含逗號、句號、冒號、分號、問號、驚嘆號以及省略號,Pi 是起始引號(花式左雙引號、書名號、左單引號),Pf 是結束引號(花式右雙引號、右書名號、右單引號)。Java 的 regex 引擎透過 Unicode 屬性逸出 \p{P} 讀取這些分類,並將它們視為單一字元類別 — 一個模式,七個子分類,Unicode 認識的每個標點都涵蓋在內,包括全形 CJK 標點如表意句號 (。)。每個子分類的宣稱都已逐字元對照 Unicode 字元資料庫 驗證,而非對照 ASCII 簡表。這裡使用的 \p{...} 屬性語法記載於 Unicode 字元類別逸出參考;Java 的 java.util.regex 與 JavaScript 共用同一套 Unicode 屬性名稱。
| 子分類 | 名稱 | 範例 |
|---|---|---|
| Pc | 連接符標點 | _ ‿ ⁔ |
| Pd | 破折號 | - – — |
| Ps | 起始標點 | ( [ { |
| Pe | 結束標點 | ) ] } |
| Po | 其他標點 | . , ; : ! ? … |
| Pi | 起始引號 | " « ' " |
| Pf | 結束引號 | " » ' ' |
三種 Java 移除標點的方法
Java 中有三種從字串移除標點的方法。第一種是 String.replaceAll("\\p{P}+", "") — 為建議做法。它具有 Unicode 感知能力,以單一線性掃描執行,+ 量詞會將連續標點塌縮,因此 !!! 會變成空字串,而非在刪除的記號之間留下空隙。第二種是 String.replaceAll("\\p{Punct}+", "") — POSIX 風格的字元類別。它比對的集合略有不同(特別是包含底線和特定 ASCII 符號),在你只需要處理 ASCII 標點時很實用。第三種是搭配 Character.isLetterOrDigit() 進行手動字元迭代 — 速度較慢、程式碼較多,而且在 Unicode 字母存在於 ASCII 範圍之外時會悄悄出錯。生產環境的 Java 程式碼應優先採用第一種做法,除非專案明確要求 POSIX 語意。
用一行 Java 程式碼移除字串中的標點
具體來說,在 Java 原始檔中:
String input = "Hello, world! It's 2024 — and prices start at $9.99."; String stripped = input.replaceAll("\\p{P}+", "");
那一行程式碼就會在一次掃描中移除每個逗號、驚嘆號、em dash 和句號。錢號保留下來 — 符號並非標點,\p{P} 不會比對 $。It's 中的撇號也被移除,因為純粹的 \p{P}+ 無法區分撇號與其他 Po 字元。em dash 之後留下兩個相鄰空格,因為 replaceAll 把符合的碼位取代為空字串;若你想要單一空格的輸出,可以再執行一輪將連續空格塌縮。
若要保留縮寫,更謹慎的 regex 會加入 lookbehind 與 lookahead 斷言,要求任何記號的兩側都必須有字母或數字:
String preserve = input.replaceAll("(?<![\\p{L}\\p{N}])\\p{P}+|(?<=[\\p{L}\\p{N}])\\p{P}+(?![\\p{L}\\p{N}])", "");
該 regex 會保護單字內部的記號 — don't 保留、state-of-the-art 保留、2020-2021 保留 — 同時仍會移除飄浮在單字邊界或空格之間的標點。這個規則有一個誠實的後果:像 the dogs' bowls 這種結尾所有格的撇號會被移除,因為後面沒有內容讓 lookahead 比對。在註解中記載這點,比隱瞞它更好。
符號、貨幣符號和表情符號並非標點
即便再完美的 Java regex 仍有尖銳的邊角:絕不能移除看起來像標點的符號。Unicode 將符號放在獨立的分類中 — Sm(數學)、Sc(貨幣)、Sk(修飾)、So(其他) — 因此錢號 $、加號 +、等號 =、版權符號 ©、@ 符號、% 符號,以及你能從手機貼上的每個表情符號,都歸類為符號而非標點。使用 [^a-zA-Z0-9 ] 作為速記的 regex 通常會一併刪除它們,從 2014 年的論壇解答複製該模式的開發人員,最後會得到 1999 tax,原來的價格已不見。
| 字元 | Unicode 分類 | \p{P} 是否比對? | Java 移除後是否保留? |
|---|---|---|---|
| $ | Sc(貨幣符號) | 否 | 是 |
| + | Sm(數學符號) | 否 | 是 |
| = | Sm(數學符號) | 否 | 是 |
| © | So(其他符號) | 否 | 是 |
| 😀 | So(其他符號) | 否 | 是 |
| , | Po(其他標點) | 是 | 否 |
| . | Po(其他標點) | 是 | 否 |
| — | Pd(破折號) | 是 | 否 |
標點與符號的區分,正是「remove punctuation from string java」這個問題需要仔細回答的全部原因。
讓每個 Java 實作踩坑的邊界情況
在 regex 進入生產環境前,有三種情況需要明確注意。
數字會失去小數點與千分位分隔符。"9.99" 會變成 "999","1,000" 會變成 "1000",因為句號和逗號都屬於標點。這就是移除標點的意義。若你的程式碼需要保留各部分,請將每次符合取代為單一空格 — replaceAll("\\p{P}+", " ") — 接著塌縮連續空格;數字會變成 "9 99" 而非黏在一起。對財務資料來說,幾乎從來不該直接移除標點;應該改用解析數字的方式。
縮寫與所有格預設會塌縮。"don't" 變成 "dont","it's" 變成 "its","James's book" 變成 "Jamess book"。前面使用 lookbehind/lookahead 的 regex 可修補多數情況,但像 "the dogs' bowls" 這種結尾所有格仍會失去撇號 — 後面沒有內容,lookahead 永遠不會觸發。老實記載此情況的 Java 檔案,比用未記載的 if 隱藏後果的版本更有用。
換行與 Tab 不受影響。Java 的 \p{P} 不會比對 \n 或 \t,因此多行輸入的形狀得以保留。在為日誌行做索引前移除標點時,這點很重要。
以「移除標點」工具驗證 Java 輸出
Java 程式碼編譯慢且在本機執行,這使得它不適合「我只想看移除標點後文字長什麼樣」這種情境。Remove Punctuation 工具在瀏覽器中進行同樣的 Unicode 分類檢查 — 精準移除七個標點子分類、不動符號和表情符號,並提供獨立開關以保護單字內部的撇號與連字號。貼上範例字串,決定縮寫與複合詞是否保留記號,按下按鈕,頁面底部的計數器會精準回報刪除了多少個碼位。
這個工具最適合作為驗證步驟。對同一輸入執行 Java regex,把 Java 輸出在未啟用任何保護開關的情況下貼進工具,確認計數器維持在零 — 代表你的 regex 與 Unicode 標準定義的移除集合相同。若 Java 版本遺失了工具保留的字元,表示你的 regex 比對過多;若工具遺失了 Java 版本保留的字元,則表示你的 regex 漏了七個子分類中的某個。此操作具等冪性,再次處理輸出結果不會改變任何東西。
舉例來說,輸入 One, two. Three! 正好包含三個標點:一個逗號、一個句號、一個驚嘆號。Java replaceAll("\\p{P}+", "") 的結果是 One two Three。把該結果在未啟用任何開關的情況下貼回工具,會得到相同字串且移除計數為零。若計數器未維持在零,代表 regex 比對的範圍超過七個標點子分類。
何時使用 Java 程式碼,何時使用工具
分工很單純。任何在生產環境執行的東西 — 資料管線、日誌清理器、索引步驟 — 寫 Java;regex 一行、測試一行,稽核軌跡留在你的版本庫。任何點擊比編譯更快的事 — 快速檢查從同事那貼來的一段文字、在程式碼審查前對 regex 做健全性檢查、向非工程師展示「移除標點」實際上是什麼意思 — 用工具。Java 程式碼是大規模可重複工作的正解;瀏覽器工具則適合用在讀完本文到撰寫 regex 之間的那個當下。
想進一步了解,請參閱 如何從 Excel 儲存格移除特殊字元。
想進一步了解,請參閱 如何在 Excel 工作表中移除重複行。