Try   HackMD

regular expression

tags: python語法

Regex 正規表示法 - 基本語法

或 (or):| 管線符號,用來將所有可能的選擇條件分隔開。例如 gray|grey 可以用來匹配 gray 或是 grey 字串。
群組 (grouping):() 小括號,用來表示作用範圍或優先順序。例如 gray|grey 和 gr(a|e)y 都同樣可以用來匹配 gray 或是 grey 字串。
量詞 (quantifier):quantifier 用來接在字符串或群組後面,表示某個條件應該出現「幾次」。常見的量詞有:

?:表示連續出現 0 次或 1 次。例如 colou?r 可以用來匹配 color 或 colour。
:表示連續出現 0 次或多次。例如 abc 可以用來匹配 ac, abc, abbc, abbbc 或 abbbbbbc。
+:表示連續出現 1 次或多次。例如 ab+c 可以用來匹配 abc, abbc, abbbc, abbbbbbc,但 ac 不符合。
{min,max}:表示至少連續出現 min 次,但最多連續出現 max 次。

Regex 正規表示法 - 字元類別 (Character Classes)

  • dot
    點號 (dot or period) . 用來匹配除了換行符號 (line breaks) \n \r 之外的任何一個字元。

    例子:
    a.c 可以用來匹配 "abc", "aGc", "a c", "a5c", "a_c", "a#c" 等字串,但不能匹配 "a\nc"。

import re bool(re.search('a.c', 'abc')) # True bool(re.search('a.c', 'a#c')) # True bool(re.search('a.c', 'a\nc')) # False
  • [\s\S] match any
    [\s\S] 用來匹配任意一個字元,包含換行符號。

    例子:
    a[\s\S]c 可以用來匹配 "abc", "aGc", "a c", "a5c", "a_c", "a#c", "a\nc" 等字串。

import re bool(re.search('a[\s\S]c', 'abc')) # True bool(re.search('a[\s\S]c', 'a#c')) # True bool(re.search('a[\s\S]c', 'a\nc')) # True
  • \w word (小寫)
    \w 用來匹配所有大小寫英文字、阿拉伯數字和底線 _。

    \w 意思同等於 [A-Za-z0-9_]。

    例子:
    pattern a\wc 可以用來匹配 "abc", "aGc", "a5c", "a_c" 等字串,但不能匹配 "a c", "a#c", "a\nc" 等字串。

import re bool(re.search('a\wc', 'abc')) # True bool(re.search('a\wc', 'a#c')) # False bool(re.search('a\wc', 'a\nc')) # False
  • \W not word(大寫)
    \W 用來匹配 \w 以外的所有字。

    \W 意思同等於 [^A-Za-z0-9_]。

    例子:
    a\Wc 可以用來匹配 "a c", "a#c", "a\nc" 等字串,但不能匹配 "abc", "aGc", "a5c", "a_c" 等字串。

import re bool(re.search('a\Wc', 'abc')) # False bool(re.search('a\Wc', 'a#c')) # True bool(re.search('a\Wc', 'a\nc')) # True
  • \d digit
    \d 用來匹配所有阿拉伯數字 0-9。

    \d 意思同等於 [0-9]。

    例子:
    a\dc 可以用來匹配 "a1c", "a5c" 等字串,但不能匹配 "abc", "aGc", "a_c", "a c", "a#c", "a\nc" 等字串。

import re bool(re.search('a\dc', 'abc')) # False bool(re.search('a\dc', 'a5c')) # True bool(re.search('a\dc', 'a\nc')) # False
  • \D not digit
    \D 用來匹配 \d 以外的所有字。

    \D 意思同等於 [^0-9]。

    例子:
    a\Dc 可以用來匹配 "abc", "aGc", "a_c", "a c", "a#c", "a\nc" 等字串,但不能匹配 "a1c", "a5c" 等字串。

import re bool(re.search('a\Dc', 'abc')) # True bool(re.search('a\Dc', 'a5c')) # False bool(re.search('a\Dc', 'a\nc')) # True
  • \s whitespace(小寫)
    \s 用來匹配所有的空白字元 (whitespace) - 空白 (space)、tab 和換行符號 \r \n。

    例子:
    a\sc 可以用來匹配 "a c", "a\nc" 等字串,但不能匹配 "abc", "aGc", "a_c", "a#c", "a1c", "a5c" 等字串。

import re bool(re.search('a\sc', 'a c')) # True bool(re.search('a\sc', 'a5c')) # False bool(re.search('a\sc', 'a\nc')) # True
  • \S not whitespace(大寫)
    \S 用來匹配 \s 以外的所有字。

    例子:
    a\Sc 可以用來匹配 "abc", "aGc", "a_c", "a#c", "a1c", "a5c" 等字串,但不能匹配 "a c", "a\nc" 等字串。

import re bool(re.search('a\Sc', 'a c')) # False bool(re.search('a\Sc', 'a5c')) # True bool(re.search('a\Sc', 'a\nc')) # False
  • [] character set
    [ ] 中括號用來表示一個字元集合 (character set),整個中括號代表一個字元,裡面的內容就是這個字元的所有可能。

    例子:
    a[abcde123]c 可以用來匹配 "abc", "a1c" 等字串,但不能匹配 "aGc", "a_c", "a#c", "a5c", "a c", "a\nc" 等字串。

    如果 [ ] 中有包含 ] 字元,你需要用反斜線 \ 來跳脫 (escape) 這一個特殊字元。

    例子:
    a[\]]c 可以用來匹配 "a]c" 字串。

import re bool(re.search('a[abcde123]c', 'abc')) # True bool(re.search('a[abcde123]c', 'a#c')) # False bool(re.search('a[abcde123]c', 'a_c')) # False
  • [^ ] negated set
    [^ ] 是 [ ] 的相反,用來匹配不在字元集合裡面的字元。

    例子:
    a[^abcde123]c 可以用來匹配 "aGc", "a_c", "a#c", "a5c", "a c", "a\nc" 等字串,但不能匹配 "abc", "a1c" 等字串。

import re bool(re.search('a[^abcde123]c', 'abc')) # False bool(re.search('a[^abcde123]c', 'a#c')) # True bool(re.search('a[^abcde123]c', 'a_c')) # True
  • [A-Z] range
    [ ] (或 [^ ]) 中還可以用 - 符號來表示連續 (range) 的好幾個字元。

    例子:
    a[a-zC-F3-7]c 其中 a-z 表示 a 到 z 所有的小寫英文字、C-F 表示大寫的 C 到 F (C D E F)、3-7 表示數字 3 到 7 (3 4 5 6 7),可以用來匹配 "abc", "a5c" 等字串,但不能匹配 "a1c", "aGc", "a_c", "a#c", "a c", "a\nc" 等字串。

import re bool(re.search('a[a-zC-F3-7]c', 'abc')) # True bool(re.search('a[a-zC-F3-7]c', 'a5c')) # True bool(re.search('a[a-zC-F3-7]c', 'aGc')) # False

Regex 正規表示法 - 錨點符號 (Anchors)

錨點符號 (anchor) 是用來表示「定位」的樣式,不用來比對字元,本身不佔據任何字元位置。

  • ^ beginning
    ^ 用來表示只匹配以 「開頭」的字串。

    例子:
    ^hello 可以用來匹配 "hello world" 字串,但不能匹配 "say hello 123",因為 hello 出現在 "hello world" 的開頭,但 "say hello 123" 的 hello 不在開頭。

    但要特別注意在「多行」的字串中,像是:

    101
    hello Mike

    ^hello 無法用來匹配上面的字串,因為其 hello 不在開頭的位置!但你可以加上 m 修飾詞 (flag) 讓 ^ 改成匹配「行首」,例如 /^hello/m 即可用來匹配上面的字串。

import re bool(re.search('^hello', 'hello world')) # True bool(re.search('^hello', 'say hello 123')) # False
  • $ end
    $ 用來表示只匹配以 「結尾」的字串。

    例子:
    foo$ 可以用來匹配 "bar foo" 字串,但不能匹配 "foo bar",因為 foo 出現在 "bar foo" 的結尾,但 "foo bar" 的 foo 不在結尾。

    但要特別注意在「多行」的字串中,像是:

    123 foo
    456

    foo$ 無法用來匹配上面的字串,因為其 foo 不在結尾的位置!但你可以加上 m 修飾詞 (flag) 讓 $ 改成匹配「行尾」,例如 /foo$/m 即可用來匹配上面的字串。

import re bool(re.search('foo$', 'bar foo')) # True bool(re.search('foo$', 'foo bar')) # False
  • \b word boundary
    \b 用來匹配單字邊界 (word boundary),表示字元的「前面」或「後面」除了空白字元 (whitespace)、標點符號 (punctuation) 或是在字串開頭或結尾外不可再有其它字元。

    word boundary 可以看作是不屬於 \w 的字元。

    例子:
    llo\b 可以用來匹配 "hello world", "hello\nworld", "hello", "hello, Mike",但不能用來匹配 "hello_world", "helloworld", "hello101"。

    \bworld\b 可以用來匹配 "hello world", "hello~world", "world",但不能用來匹配 "helloworld", "01world"。

import re bool(re.search(r'llo\b', 'hello world')) # True bool(re.search(r'llo\b', 'hello_world')) # False
  • \B not word boundary
    \B 則是相對於 \b,用來匹配非單字邊界 (word boundary)。

    例子:
    llo\B 可以用來匹配 "hello_world", "helloworld", "hello101",但不能用來匹配 "hello world", "hello\nworld", "hello", "hello, Mike"。

    \Bworld\B 可以用來匹配 "123worldxyz",但不能用來匹配 "helloworld", "hello world", "world"。

import re bool(re.search(r'llo\B', 'hello world')) # False bool(re.search(r'llo\B', 'hello_world')) # True

Regex 正規表示法 - 特殊字元 (Escaped Characters)

若字元為特殊符號,在 Regex 中具有其他意義,可以用反斜線的跳脫字符 \ 將特殊字元還原成其字面上的意思,換句話說跳脫字符可以將特殊符號的特殊意義去除!

例如使用 . 這時的 . 是代表真的點號,而不是 Regex 語法中的特殊意義;另外像是 + 也是代表加號,而不是指量詞。

特殊字元列表

Regex 正規表示法 - 群組與環顧 (Groups & Lookaround)

Capturing Group ( )
在 Regex 的 pattern 中,用小括號 ( ) 圈住的部分表示一個子樣式 (subexpression),也稱作是一個群組 (group)。

( ) 有幾個用途:

  1. pattern 裡面的小括號可以用來擷取 (capture) 出子字串 (substring),你最後可以從匹配群組 (capturing group) 的結果中取得這些子字串。
  2. pattern 裡面如果有小括號,那麼括號中匹配到的子字串是可以在 pattern 中再被取出來使用的,這行為稱作回朔 (backreference)。
  3. 小括號在 Regex 當中也有 group 的意思,用在把括號內的條件式當作是一個整體。例如 (foo){2,} 表示匹配連續出現兩次以上的 foo 字串,而不是像 foo{2,} 中的量詞 {2,} 只會作用在最後的 o 字元。
import re match = re.search(r'(hello \S+)', 'This is a hello world!') print(match.group(1)) # hello world!

Regex 正規表示法 - 量詞(Quantifiers)

量詞 (quantifier) 表示某個條件需要出現的次數。

  • Plus +
    + 表示連續出現 1 次或多次。

    例子:
    pattern ab+c 可以用來匹配 "abc", "abbc", "abbbc", "abbbbbbc" 等字串,但 "ac", "adc" 不符合。

import re match = re.search(r'a+', 'caaaaaaandy') print(match.group()) # aaaaaaa
  • Star *
    * 表示連續出現 0 次或多次。

    例子:
    pattern ab*c 可以用來匹配 "ac", "abc", "abbc", "abbbc", "abbbbbbc" 等字串,但 "adc", "a123" 不符合。

import re match = re.search(r'bo*', 'A ghost booooed') print(match.group()) # boooo
  • Quantifier {min,max} {n} {min,}
    Quantifier 有三種寫法:

    {min,max} 表示至少連續出現 min 次,但最多連續出現 max 次。
    {n} 表示要出現 n 次。
    {min,} 表示至少連續出現 min 次。

    例子:
    pattern ab{1,3}c 可以用來匹配 "abc", "abbc", "abbbc" 等字串,但 "ac", "abbbbbbc", "adc", "a123" 不符合。

    pattern ab{3}c 可以用來匹配 "abbbc" 等字串,但 "ac", "abc", "abbc", "abbbbbbc", "adc", "a123" 不符合。

    pattern ab{3,}c 可以用來匹配 "abbbc", "abbbbbbc" 等字串,但 "abc", "abbc", "ac", "adc", "a123" 不符合。

import re match = re.search(r'a{1,3}', 'caaaaaaandy') print(match.group()) # aaa
  • Optional ?
    ? 表示出現 0 次或 1 次。

    例子:
    pattern ab?c 可以用來匹配 "ac", "abc" 等字串,但 "abbc", "abbbc", "abbbbbbc", "adc", "a123" 不符合。

import re match = re.search(r'e?le?', 'angel') print(match.group()) # el
  • Lazy ?
    對於指定的量詞,Regex 的匹配引擎在預設上,採能匹配越多字就盡量匹配,這一個特性叫做 greedy (貪婪)。

    例如,用 pattern (.+)(\d+) 來匹配字串 "abcd1234",group 1 會得到 "abcd123",group 2 會得到 "4",正是因為 greedy 的特性讓 .+ 匹配到盡可能多的字。

    ? 還有另外一個用法,接在量詞後面 (+?, *?, {min,max}?, ??) 表示 lazy (非貪婪, 懶惰) 模式,會使 Regex 的匹配引擎變成以匹配越少字為原則,盡可能匹配短結果。

    例如,用 pattern (.+?)(\d+) 來匹配字串 "abcd1234",group 1 會得到 "abcd",group 2 會得到 "1234",正是因為 lazy 的特性讓 .+ 匹配到盡可能少的字。

import re match = re.search(r'<.+?>', '<em>Hello World</em>') print(match.group()) # <em>

Python的re模塊

  • findall(pattern, string, flags=0)
    該函數可以以列表的形式返回所有匹配模式的項:
pat = '[a-zA-Z]+' text = 'Hello, hm...this is Tom speaking, who are you?' re.findall(pat, text) # result ['Hello', 'hm', 'this', 'is', 'Tom', 'speaking', 'who', 'are', 'you']
  • match(pattern, string, flags=0)
    match函數從字符串的開始進行匹配,匹配成功則返回一個MatchObject,否則返回None。
re.match('h', 'hello') # result <re.Match object; span=(0, 1), match='h'>
  • search(pattern, string, flags=0)
    search函數和match函數很相像,不同之處就是search函數並不是從字符串的開始處進行匹配,而是會查找整個字符串。
re.search('e', 'hello') # result <re.Match object; span=(1, 2), match='e'>
  • split(pattern, string, maxsplit=0, flags=0)
text = 'one, two...ten') re.split('[,. ]+', text) # result ['one', 'two', 'three'] # use 'maxsplit' re.split('[,. ]+', text, maxsplit=1) # result ['one', 'two...ten']

Regex Examples

  • Email Regex / Email 正規表示式
/^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/
  • URL Regex / 網址正規表示式
/^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/
  • IP Address Regex / IP 位址正規表示式
/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/
  • HTML Tag Regex / HTML Tag 正規表示式
/^<([a-z]+)([^<]+)*(?:>(.*)<\/\1>|\s+\/>)$/