1. 程式人生 > 程式設計 >專門為初學者編寫的正則表示式入門教程

專門為初學者編寫的正則表示式入門教程

這是一篇翻譯文章。我學過很多次正則表示式,總是學了忘,忘了學,一到用的時候還是隻能靠搜尋引擎。

這回看到這個正則教程,感覺非常驚喜。嘗試翻譯了一遍,譯得不好,大家可以看原文,很容易理解。

原文地址:https://refrf.shreyasminocha.me/

1 介紹

正則表示式允許定義一種模式,並通過這種模式針對字串執行對應的操作。與模式匹配的子字串稱為“匹配”。

正則表示式是定義搜尋模式的一串字元。

正則表示式主要用在如下場景:

  • 輸入驗證
  • 查詢替換操作
  • 高階字串操作
  • 檔案搜尋或重新命名
  • 白名單和黑名單

正則表示式不太適合用在這些場景:

  • XML 或 HTML 解析
  • 完全匹配的日期

有許多實現正則匹配的引擎,每種都有自己的特性。這本書將避免討論(不同引擎之間的)特性差異,而是隻討論在大多數情況下不同引擎都共有的特徵。

整本書中的示例使用JavaScript。因此,這本書可能會稍微偏向 JavaScript 的正則引擎。

2 基礎

正則表示式通常格式化為 /<rules>/<flags>,通常為了簡潔而省略後面的 /<flags>。關於 flag 我們將在下一章詳細討論。

讓我們從/p/g 這個正則表示式開始。現在,請將 /g flag 視為固定不變的。

  • /p/g

專門為初學者編寫的正則表示式入門教程

如我們所見,/p/g 匹配所有小寫的 p 字元。

注意

預設情況下,正則表示式區分大小寫。

在輸入字串中找到的正則表示式模式的例項稱為“匹配”。

  • /pp/g

專門為初學者編寫的正則表示式入門教程

3 字元組

可以從一組字元中匹配一個字元。

  • /[aeiou]/g

專門為初學者編寫的正則表示式入門教程

[aeiou]/g 匹配輸入字串中的所有母音。

下面是另一個例子:

  • /p[aeiou]t/g

專門為初學者編寫的正則表示式入門教程

我們匹配一個 p,後跟一個母音,然後是一個 t。

有一個更直觀的快捷方式,可以在一個連續的範圍內匹配一個字元。

  • /[a-z]/g

專門為初學者編寫的正則表示式入門教程

警告

表示式 /[a-z]/g 只匹配一個字元。在上面的示例中,每個字元都有一個單獨的匹配項。不是整個字串匹配。

我們也可以在正則表示式中組合範圍和單個字元。

  • /[A-Za-z0-9_-]/g

專門為初學者編寫的正則表示式入門教程

我們的正則表示式 /[A-Za-z0-9_-]/g

匹配一個字元,該字元必須(至少)是以下字元之一:

  • A-Z
  • a-z
  • 0-9
  • _ 或者 -

我們也可以“否定”這些規則:

  • /[^aeiou]/g

專門為初學者編寫的正則表示式入門教程

/[aeiou]/g/[^aeiou]/g 之間的唯一區別是 ^ 緊跟在左括號之後。其目的是"否定"括號中定義的規則。它表示的意思是:

匹配任何不屬於a、e、i、o和 u 的字元

3.1 例子

非法的使用者名稱字元

  • /[^a-zA-Z_0-9-]/g

專門為初學者編寫的正則表示式入門教程

指定字元

/[A-HJ-NP-Za-kmnp-z2-9]/g

專門為初學者編寫的正則表示式入門教程

4 字元轉義

字元轉義是對某些通用字元類的簡略表達方式。

4.1 數字字元 \d

轉義符 \d 表示匹配數字字元 0-9。等同於 [0-9]

  • /\d/g (這裡請仔細看)

專門為初學者編寫的正則表示式入門教程

  • /\d\d/g

專門為初學者編寫的正則表示式入門教程

\D\d 的反面,相當於[^0-9]

  • /\D/g

專門為初學者編寫的正則表示式入門教程

4.2 單詞字元 \w

轉義符 \w 匹配單詞字元。包括:

  • 小寫字母 a-z
  • 大寫字母 A-Z
  • 數字 0-9
  • 下劃線 _

等價於 [a-zA-Z0-9_]

  • /\w/g

專門為初學者編寫的正則表示式入門教程

  • /\W/g

專門為初學者編寫的正則表示式入門教程

4.3 空白字元 \s

轉義符 \s匹配空白字元。具體匹配的字符集取決於正則表示式引擎,但大多數至少包括:

  • 空格
  • tab 製表符 \t
  • 回車 \r
  • 換行符 \n
  • 換頁 \f

其他還可能包括垂直製表符(\v)。Unicode自識別引擎通常匹配分隔符類別中的所有字元。然而,技術細節通常並不重要。

  • /\s/g

專門為初學者編寫的正則表示式入門教程

  • /\S/g (大寫 s)

專門為初學者編寫的正則表示式入門教程

4.4 任意字元 .

雖然不是典型的字元轉義。. 可以匹配任意1個字元。(除換行符 \n 以外,通過 dotall 修飾符,也可以匹配換行符 \n)

  • /./g

專門為初學者編寫的正則表示式入門教程

5 轉義

在正則表示式中,有些字元有特殊的含義,我們將在這一章中進行探討:

  • |
  • {,}
  • (,)
  • [,]
  • ^,$
  • +,*,?
  • \
  • . 只在字元類中的字面量。
  • - : 有時是字元類中的特殊字元。

當我們想通過字面意思匹配這些字元時,我們可以再這些字元前面加 \ “轉義”它們。

  • /\(paren\)/g

專門為初學者編寫的正則表示式入門教程

  • /(paren)/g

專門為初學者編寫的正則表示式入門教程

  • /example\.com/g

專門為初學者編寫的正則表示式入門教程

  • /example.com/g

專門為初學者編寫的正則表示式入門教程

  • /A\+/g

專門為初學者編寫的正則表示式入門教程

  • /A+/g

專門為初學者編寫的正則表示式入門教程

  • /worth \$5/g

專門為初學者編寫的正則表示式入門教程

  • /worth $5/g

專門為初學者編寫的正則表示式入門教程

5.1 例子

JavaScript 內聯註釋

  • /\/\/.*

專門為初學者編寫的正則表示式入門教程

星號包圍的子串

  • /*[^\*]*\*

專門為初學者編寫的正則表示式入門教程

第一個和最後一個星號是字面上的,所有他們要用 \* 轉義。字符集裡面的星號不需要被轉義,但為了清楚起見,我還是轉義了它。緊跟在字符集後面的星號表示字符集的重複,我們將在後面的章節中對此進行探討。

6 組

顧名思義,組是用來“組合”正則表示式的元件的。這些組可用於:

  • 提取匹配的子集
  • 重複分組任意次數
  • 參考先前匹配的子字串
  • 增強可讀性
  • 允許複雜的替換

這一章我們先學組如何工作,之後的章節還會有更多例子。

6.1 捕獲組

捕獲組用(…)表示。下面是一個解釋性的例子:

  • /a(bcd)e/g

專門為初學者編寫的正則表示式入門教程

捕獲組允許提取部分匹配項。

  • /\{([^{}]*)\}/g

專門為初學者編寫的正則表示式入門教程

通過語言的正則函式,您將能夠提取括號之間匹配的文字。

捕獲組還可以用於對正則表示式進行部分分組,以便於重複。雖然我們將在接下來的章節中詳細介紹重複,但這裡有一個示例演示了組的實用性。

  • /a(bcd)+e/g

專門為初學者編寫的正則表示式入門教程

其他時候,它們用於對正則表示式的邏輯相似部分進行分組,以提高可讀性。

  • /(\d\d\d\d)-W(\d\d)/g

專門為初學者編寫的正則表示式入門教程

6.2 回溯

回溯允許引用之前捕獲的子字串。

匹配第一組可以使用 \1,匹配第二組可以使用 \2,依此類推…

  • /([abc])×\1×\1/g

專門為初學者編寫的正則表示式入門教程

不能使用回溯來減少正則表示式中的重複。它們指的是組的匹配,而不是模式。

  • /[abc][abc][abc]/g

專門為初學者編寫的正則表示式入門教程

  • /[abc]\1\1/g

專門為初學者編寫的正則表示式入門教程

下面是一個演示常見用例的示例:

  • /\w+([,|])\w+\1\w+/g

專門為初學者編寫的正則表示式入門教程

這不能通過重複的字元類來實現。

  • /\w+[,|]\w+[,|]\w+/g

專門為初學者編寫的正則表示式入門教程

6.3 非捕獲組

非捕獲組與捕獲組非常相似,只是它們不建立“捕獲”。而是採取形式 (?: ...)

非捕獲組通常與捕獲組一起使用。也許您正在嘗試使用捕獲組提取匹配的某些部分。而你可能希望使用一個組而不擾亂捕獲順序,這時候你應該使用非捕獲組。

6.4 例子

查詢字串引數

  • /^\?(\w+)=(\w+)(?:&(\w+)=(\w+))*$/g

專門為初學者編寫的正則表示式入門教程

我們單獨匹配第一組鍵值對,因為這可以讓我麼使用 & 分隔符, 作為重複組的一部分。

(基礎的) HTML 標籤

根據經驗,不要使用正則表示式來匹配 XML/HTML。不過,我還是提供相關的一個例子:

  • /<([a-z]+)+>(.*)<\/\1>/gi

專門為初學者編寫的正則表示式入門教程

姓名

查詢:\b(\w+) (\w+)\b

替換:

在替換操作,經常使用 2;捕獲使用 \1,\2

替換之前

John Doe
Jane Doe
Sven Svensson
Janez Novak
Janez Kranjski
Tim Joe

替換之後

Doe,John
Doe,Jane
Svensson,Sven
Novak,Janez
Kranjski,Janez
Joe,Tim

回溯和複數

查詢: \bword(s?)\b

替換: phrase$1

替換之前

This is a paragraph with some words.
Some instances of the word "word" are in their plural form: "words".

替換之後

This is a paragraph with some phrases.

Yet,some are in their singular form: "phrase".

7 重複

重複是一個強大而普遍的正則表示式特性。在正則表示式中有幾種表示重複的方法。

7.1 可選項

我們可以使用 ?將某一部分設定成可選的(0或者1次)。

  • /a?/g

專門為初學者編寫的正則表示式入門教程

另一個例子:

  • /https?/g

專門為初學者編寫的正則表示式入門教程

我們還可以讓捕獲組和非捕獲組程式設計可選的。

  • /url: (www\.)?example\.com/g

專門為初學者編寫的正則表示式入門教程

7.2 零次或者多次

如果我們希望匹配零個或多個標記,可以用 * 作為字尾。

  • /a*/g

專門為初學者編寫的正則表示式入門教程

我們的正則表示式甚至匹配一個空字串。

7.3 一次或者多次

如果我們希望匹配 1 個或多個標記,可以用 + 作為字尾。

  • /a+/g

專門為初學者編寫的正則表示式入門教程

7.4 精確的 x 次

如果我們希望匹配特定的標記正好x次,我們可以新增{x}字尾。這在功能上等同於複製貼上該標記 x 次。

  • /a{3}/g

專門為初學者編寫的正則表示式入門教程

下面是匹配大寫的六個字元的十六進位制顏色程式碼的例子。

  • /#[0-9A-F]{6}/g

專門為初學者編寫的正則表示式入門教程

這裡,標記 {6} 應用於字符集 [0-9A-F]。

7.5 最小次和最大次之間

如果我們希望在最小次和最大次之間匹配一個特定標記,可以在這個標記後新增 {min,max}

  • /a{2,4}/g

專門為初學者編寫的正則表示式入門教程

警告

{min,max} 中逗號後面不要有空格。

7.6 最少 x 次

如果我們希望匹配一個特定的標記最少 x 次,可以在標記後新增 {x,}。 和 {min,max} 類似,只是沒有上限了。

  • /a{2,}/g

專門為初學者編寫的正則表示式入門教程

7.7 貪婪模式的注意事項

正則表示式預設使用貪婪模式。在貪婪模式下,會盡可能多的匹配符合要求的字元。

  • /a*/g

專門為初學者編寫的正則表示式入門教程

  • /".*"/g

專門為初學者編寫的正則表示式入門教程

在**重複操作符(?,*,+,...)**後面新增 ,可以讓匹配變“懶”。

  • /".*?"/g

專門為初學者編寫的正則表示式入門教程

在這裡,這也可以通過使用[^"]代替。(這是最好的做法)。

  • /"[^"]*"/g

專門為初學者編寫的正則表示式入門教程

懶惰,意味著只要條件滿足,就立即停止;但貪婪意味著只有條件不再滿足才停止。

-Andrew S on StackOverflow

  • /<.+>/g

專門為初學者編寫的正則表示式入門教程

  • /<.+?>/g

專門為初學者編寫的正則表示式入門教程

7.8 例子

比特幣地址

  • /([13][a-km-zA-HJ-NP-Z0-9]{26,33})/g (思考: {26,33}?呢)

專門為初學者編寫的正則表示式入門教程

Youtube 視訊

  • /(?:https?:\/\/)?(?:www\.)?youtube\.com\/watch\?.*?v=([^&\s]+).*/gm

專門為初學者編寫的正則表示式入門教程

我們可以使用錨點調整表示式不讓它匹配最後一個不正確的連結,之後我們會接觸到。

8 交替

交替允許匹配幾個短語中的一個。這比僅限於單個字元的字元組更加強大。

使用管道符號 | 把多個短語之間分開

  • /foo|bar|baz/g

專門為初學者編寫的正則表示式入門教程

匹配 foo,bar,和 baz 中的一個。

如果正則中只有一部分需要“交替”,可以使用組進行包裹,捕獲組和非捕獲組都可以。

  • /Try (foo|bar|baz)/g

專門為初學者編寫的正則表示式入門教程

Try 後面跟著 foo,和 baz 中的一個。

匹配 100-250 中間的數字:

  • /1\d\d|2[0-4]\d|250/g

專門為初學者編寫的正則表示式入門教程

這個可以使用 Regex Numeric Range Generator 工具生成。

例子

十六進位制顏色

讓我們改進一下之前十六進位制顏色匹配的例子。

  • /#[0-9A-F]{6}|[0-9A-F]{3}

專門為初學者編寫的正則表示式入門教程

[0-9A-F]{6} 要放在[0-9A-F]{3}的前面,這一點非常重要。否則:

  • /#([0-9A-F]{3}|[0-9A-F]{6})/g

專門為初學者編寫的正則表示式入門教程

小提示

正則表示式引擎是從左邊到右邊的嘗試交替的。

羅馬數字

  • /^M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$/g

專門為初學者編寫的正則表示式入門教程

9 修飾符

修飾符允許我們把正則表示式分成不同的 "模式"。

修飾符是 /pattern/ 後面的部分。

不同引擎支援不同的修飾符。在這裡我們只討論最常見修飾符。

9.1 全域性修飾符(g)

到現在為止,所有的例子都設定了全域性修飾符。如果不啟用全域性修飾符,正則表示式匹配第一個以後將不再匹配其他任何字元。

  • /[aeiou]/g

專門為初學者編寫的正則表示式入門教程

  • /[aeiou]/

專門為初學者編寫的正則表示式入門教程

9.2 不區分大小寫修飾符(i)

顧名思義,啟用這個修飾符會使正則在匹配時不區分大小寫。

  • /#[0-9A-F]{6}/i

專門為初學者編寫的正則表示式入門教程

  • /#[0-9A-F]{6}/

專門為初學者編寫的正則表示式入門教程

  • /#[0-9A-Fa-f]{6}/

專門為初學者編寫的正則表示式入門教程

9.3 多行模式修飾符(m)

有限支援

在 Ruby 中,m 修飾符是執行其他的函式。

多行修飾符與正在在處理包含換行符的“多行”字串時對錨點的處理有關。預設情況下,/^foo$/只匹配 “foo”。

我們可能希望它在多行字串中的一行也能匹配 foo。

我們拿 "bar\nfoo\nbaz" 舉例子:

bar foo baz

如果沒有 m 修飾符,上面的字串會被當做單行 bar\nfoo\nbaz,正則表示式 ^foo$ 匹配不到任何字元。

如果有 m 修飾符,上面的字串會被當做 3 行。 ^foo$ 可以匹配到中間那一行。

9.4 Dot-all修飾符 (s)

有限支援

ES2018 之前的 JavaScript 不支援這個修飾符。 Ruby 也不支援這個修飾,而是用 m 表示。

.通常匹配除換行符以外的任何字元。使用dot all修飾符後,它也可以匹配換行符。

10 錨點

錨點本身不匹配任何東西。但是,他們會限制匹配出現的位置。

你可以把錨點當做是 "不可見的字元"。

10.1 行首 ^

在正則開始時插入^ 號,使正則其餘部分必須從字串開始的地方匹配。你可以把它當成始終要在字串開頭匹配一個不可見的字元。

  • /^p/g

專門為初學者編寫的正則表示式入門教程

10.2 行尾

在正則結尾時插入$ 號,類似於行首符。你可以把它當成始終要在字串結尾匹配一個不可見的字元。

  • /p$/g

專門為初學者編寫的正則表示式入門教程

^$錨點經常一起使用,以確保正則和字串整個匹配,而不僅僅是部分匹配。

  • /^p$/g

專門為初學者編寫的正則表示式入門教程

讓我們回顧一下重複中的一個例子,並在正則的末尾新增兩個錨點。

  • /^https?$/g

專門為初學者編寫的正則表示式入門教程

如果沒有這 2 個錨點, http/2shttp 也會被匹配。

10.3 字邊界 \b

字邊界是一個字元和非詞字元之間的位置。

字邊界錨點 \b,匹配字元和非詞字元之間存在的假想不可見字元。

  • /\bp/g

專門為初學者編寫的正則表示式入門教程

提示

字元包括 a-z,A-Z,0-9,和_.

  • /\bp\b/g

專門為初學者編寫的正則表示式入門教程

  • /\bcat\b/g

專門為初學者編寫的正則表示式入門教程

還有一個非字邊界錨 \B

顧名思義,它匹配除字邊界之外的所有內容。

  • /\Bp/g

專門為初學者編寫的正則表示式入門教程

  • /\Bp\B/g

專門為初學者編寫的正則表示式入門教程

小提示

^…$\b…\b是常見的模式,您幾乎總是需要這 2 個防止意外匹配。

10.4 例子

尾部空格

  • /\s+$/gm

專門為初學者編寫的正則表示式入門教程

markdown 標題

  • /^## /gm

專門為初學者編寫的正則表示式入門教程

沒有錨點:

  • /## /gm

專門為初學者編寫的正則表示式入門教程

11 零寬斷言(lookaround)

零寬斷言可用於驗證條件,而不匹配任何文字。

你只能看,不能動。

  • 先行斷言(lookhead)
    • 正向(?=…)
    • 負向(?!…)
  • 先行斷言(lookbehind)
    • 正向(?<=…)
    • 負向(?<!…)

11.1 先行斷言(lookhead)

正向(positive)

  • /_(?=[aeiou])/g

專門為初學者編寫的正則表示式入門教程

注意後面的字元是如何不匹配的。可以通過正面前看得到證實。

  • /(.+)_(?=[aeiou])(?=\1)/g

專門為初學者編寫的正則表示式入門教程

正則引擎在 _ 使用了 (?=[aeiou])(?=\1) 進行檢查。

  • /(?=.*#).*/g

專門為初學者編寫的正則表示式入門教程

負向(Negative)

  • /_(?![aeiou])/g

專門為初學者編寫的正則表示式入門教程

  • /^(?!.*#).*$/g

專門為初學者編寫的正則表示式入門教程

如果沒有錨點,將匹配每個示例中沒有#的部分。

負向的先行斷言常常用於防止匹配特定短語。

  • /foo(?!bar)/g

專門為初學者編寫的正則表示式入門教程

  • /---(?:(?!---).)*---/g

專門為初學者編寫的正則表示式入門教程

11.2 例子

密碼驗證

/^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[a-zA-Z]).{8,}$/

專門為初學者編寫的正則表示式入門教程

零寬斷言可用於驗證多個條件。

帶引號的字串

  • /(['"])(?:(?!\1).)*\1/g

專門為初學者編寫的正則表示式入門教程

如果沒有先行斷言,我們最多隻能做到這樣:

  • /(['"])[^'"]*\1/g

專門為初學者編寫的正則表示式入門教程

12 進階例子

JavaScript 註釋

  • /\/\*[\s\S]*?\*\/|\/\/.*/g

專門為初學者編寫的正則表示式入門教程

[\s\S]是一種匹配任何字元(包括換行符)的技巧。我們避免使用dot-all 修飾符,因為我們需要使用. 表示單行註釋。

24小時時間

  • /^([01]?[0-9]|2[0-3]):[0-5][0-9](:[0-5][0-9])?$/g

專門為初學者編寫的正則表示式入門教程

IP 地址

  • /\b(?:(?:2(?:[0-4][0-9]|5[0-5])|[0-1]?[0-9]?[0-9])\.){3}(?:(?:2([0-4][0-9]|5[0-5])|[0-1]?[0-9]?[0-9]))\b/g

專門為初學者編寫的正則表示式入門教程

元標籤

  • /<Example source="(.*?)" flags="(.*?)">/gm

專門為初學者編寫的正則表示式入門教程

替換: <Example regex={/$1/$2}>

浮點數

  • 可選符號

  • 可選整數部分

  • 可選小數部分

  • 可選指數部分

  • /^([+-]?(?=\.\d|\d)(?:\d+)?(?:\.?\d*))(?:[eE]([+-]?\d+))?$/g

專門為初學者編寫的正則表示式入門教程

正向的先行斷言 (?=\.\d|\d) 確保不會匹配 ..

HSL顏色

從0到360的整數

  • /^0*(?:360|3[0-5]\d|[12]?\d?\d)$/g

專門為初學者編寫的正則表示式入門教程

百分比

  • /^(?:100(?:\.0+)?|\d?\d(?:\.\d+)?)%$/g

專門為初學者編寫的正則表示式入門教程

HSL 和 百分比

  • /^hsl\(\s*0*(?:360|3[0-5]\d|[12]?\d?\d)\s*(?:,\s*0*(?:100(?:\.0+)?|\d?\d(?:\.\d+)?)%\s*){2}\)$/gi

專門為初學者編寫的正則表示式入門教程

13 下一步

如果你像進一步學習正則表示式及其工作原理:

  • awesome-regex
  • regex tag on StackOverflow
  • StackOverflow RegEx FAQ
  • r/regex
  • RexEgg
  • Regular-Expressions.info
  • Regex Crossword
  • Regex Golf

謝謝閱讀!新增微信:手邊位元組