當前位置: 首頁>>代碼示例 >>用法及示例精選 >>正文


Ruby Regexp類用法及代碼示例


本文簡要介紹ruby語言中 Regexp類 的用法。

Regexp 包含一個正則表達式,用於將模式與字符串匹配。正則表達式是使用 /.../%r{...} 文字以及由 Regexp::new 構造函數創建的。

您可以使用以下命令顯式創建 Regexp 對象:

正則表達式(regexp s)是說明字符串內容的模式。它們用於測試字符串是否包含給定模式,或提取匹配的部分。它們是使用 / pat /%r{ pat } 文字或 Regexp.new 構造函數創建的。

正則表達式通常用正斜杠 (/) 分隔。例如:

/hay/ =~ 'haystack'   #=> 0
/y/.match('haystack') #=> #<MatchData "y">

如果一個字符串包含它被稱為 match 的模式。文字字符串匹配自身。

這裏‘haystack’ 不包含模式‘needle’,因此不匹配:

/needle/.match('haystack') #=> nil

這裏‘haystack’ 包含模式‘hay’,所以它匹配:

/hay/.match('haystack')    #=> #<MatchData "hay">

具體來說,/st/ 要求字符串包含字母 s 後跟字母 t ,因此它也匹配 haystack

=~ Regexp#match

模式匹配可以通過使用=~ 運算符或 Regexp#match 方法來實現。

=~ 運算符

=~ 是 Ruby 的基本 pattern-matching 運算符。當一個操作數是正則表達式而另一個是字符串時,則正則表達式用作匹配字符串的模式。 (此運算符由 Regexp String 等效定義,因此 String Regexp 的順序無關緊要。其他類可能有不同的 =~ 實現。)如果找到匹配項,則運算符返回字符串中第一個匹配的索引,否則返回 nil

/hay/ =~ 'haystack'   #=> 0
'haystack' =~ /hay/   #=> 0
/a/   =~ 'haystack'   #=> 1
/u/   =~ 'haystack'   #=> nil

使用帶有 String Regexp =~ 運算符,在成功匹配後設置$~ 全局變量。 $~ 包含一個 MatchData 對象。 Regexp.last_match 等同於 $~

Regexp#match 方法

match 方法返回一個 MatchData 對象:

/st/.match('haystack')   #=> #<MatchData "st">

元字符和轉義符

以下是 metacharacters ( , ) , [ , ] , { , } , . , ? , + , * 。當它們出現在一個模式中時,它們具有特定的含義。要從字麵上匹配它們,它們必須是backslash-escaped。要從字麵上匹配反斜杠,backslash-escape 它:\\

/1 \+ 2 = 3\?/.match('Does 1 + 2 = 3?') #=> #<MatchData "1 + 2 = 3?">
/a\\\\b/.match('a\\\\b')                    #=> #<MatchData "a\\b">

模式的行為類似於雙引號字符串,並且可以包含相同的反斜杠轉義符(\s 的含義不同,但是,請參見下文)。

/\s\u{6771 4eac 90fd}/.match("Go to 東京都")
    #=> #<MatchData " 東京都">

可以使用#{...} 構造將任意 Ruby 表達式嵌入到模式中。

place = "東京都"
/#{place}/.match("Go to 東京都")
    #=> #<MatchData "東京都">

字符類

character class 用方括號([])分隔,並列出匹配中該點可能出現的字符。 /[ab]/ 表示 ab ,而不是 /ab/ 表示 a 後跟 b

/W[aeiou]rd/.match("Word") #=> #<MatchData "Word">

在字符類中,連字符 (-) 是表示包含字符範圍的元字符。 [abcd] 等同於 [a-d] 。一個範圍後麵可以跟另一個範圍,因此 [abcdwxyz] 等價於 [a-dw-z] 。範圍或單個字符出現在字符類中的順序無關緊要。

/[0-9a-f]/.match('9f') #=> #<MatchData "9">
/[9f]/.match('9f')     #=> #<MatchData "9">

如果字符類的第一個字符是插入符號 (^),則該類被反轉:它匹配任何命名的字符 except

/[^a-eg-z]/.match('f') #=> #<MatchData "f">

一個字符類可能包含另一個字符類。這本身沒有用,因為 [a-z[0-9]] 說明了與 [a-z0-9] 相同的集合。但是,字符類也支持 && 運算符,該運算符對其參數執行集合交集。兩者可以組合如下:

/[a-w&&[^c-g]z]/ # ([a-w] AND ([^c-g] OR z))

這相當於:

/[abh-w]/

以下元字符的行為也類似於字符類:

  • /./ - 除換行符以外的任何字符。

  • /./m - 任何字符(m 修飾符啟用多行模式)

  • /\w/ - 一個單詞字符([a-zA-Z0-9_])

  • /\W/ - 非單詞字符 ([^a-zA-Z0-9_])。如果使用帶有/i 修飾符的/\W/,請查看Bug #4044

  • /\d/ - 一個數字字符([0-9])

  • /\D/ - 非數字字符([^0-9])

  • /\h/ - 一個十六進製字符([0-9a-fA-F])

  • /\H/ - 非十六進製字符 ([^0-9a-fA-F])

  • /\s/ - 一個空格字符:/[ \t\r\n\f\v]/

  • /\S/ - 非空白字符:/[^ \t\r\n\f\v]/

  • /\R/ - A linebreak: \n , \v , \f , \r , \u0085 (NEXT LINE), \u2028 (LINE SEPARATOR), \u2029 (段落分隔符) or \r\n .

POSIX bracket expressions 也類似於字符類。它們為上述內容提供了一種可移植的替代方案,另外還有一個好處是它們包含非 ASCII 字符。例如,/\d/ 僅匹配 ASCII 十進製數字 (0-9);而/[[:digit:]]/ 匹配Unicode Nd 類別中的任何字符。

  • /[[:alnum:]]/ - 字母和數字字符

  • /[[:alpha:]]/ - 字母字符

  • /[[:blank:]]/ - 空格或製表符

  • /[[:cntrl:]]/ - 控製字符

  • /[[:digit:]]/ - 數字

  • /[[:graph:]]/ - 非空白字符(不包括空格、控製字符和類似字符)

  • /[[:lower:]]/ - 小寫字母字符

  • /[[:print:]]/ - 類似於 [:graph:],但包含空格字符

  • /[[:punct:]]/ - 標點符號

  • /[[:space:]]/ - 空白字符([:blank:]、換行符、回車符等)

  • /[[:upper:]]/ - 大寫字母

  • /[[:xdigit:]]/ - 十六進製數字中允許的數字(即 0-9a-fA-F)

Ruby 還支持以下非 POSIX 字符類:

  • /[[:word:]]/ - 以下 Unicode 通用類別之一中的字符 LetterMarkNumberConnector_Punctuation

  • /[[:ascii:]]/ - ASCII 字符集中的一個字符

    # U+06F2 is "EXTENDED ARABIC-INDIC DIGIT TWO"
    /[[:digit:]]/.match("\u06F2")    #=> #<MatchData "\u{06F2}">
    /[[:upper:]][[:lower:]]/.match("Hello") #=> #<MatchData "He">
    /[[:xdigit:]][[:xdigit:]]/.match("A6")  #=> #<MatchData "A6">

重複

到目前為止說明的構造匹配單個字符。它們後麵可以跟一個重複元字符來指定它們需要出現多少次。這樣的元字符稱為 quantifiers

  • * - 零次或多次

  • + - 一次或多次

  • ? - 零次或一次(可選)

  • { n } - 正好 n

  • { n ,} - n 或更多次

  • {, m } - m 或更少次

  • { n , m } - 至少 n 和最多 m

至少一個大寫字符 (‘H’),至少一個小寫字符 (‘e’),兩個 ‘l’ 字符,然後是一個 ‘o’:

"Hello".match(/[[:upper:]]+[[:lower:]]+l{2}o/) #=> #<MatchData "Hello">

貪心匹配

默認情況下,重複為greedy:匹配盡可能多的匹配項,同時仍允許整體匹配成功。相比之下,lazy 匹配使整體成功所需的匹配數量最少。大多數貪心的元字符可以通過使用 ? 來變得懶惰。對於{n} 模式,因為它指定要匹配的確切字符數而不是可變數量的字符,所以? 元字符反而使重複模式成為可選的。

下麵的兩種模式都匹配字符串。第一個使用貪心量詞,所以‘.+’匹配‘<a><b>’;第二個使用惰性量詞,所以'.+?'匹配'<a>':

/<.+>/.match("<a><b>")  #=> #<MatchData "<a><b>">
/<.+?>/.match("<a><b>") #=> #<MatchData "<a>">

占有欲匹配

後跟 + 的量詞匹配 possessively :一旦匹配,它就不會回溯。他們表現得像貪心的量詞,但在匹配後他們拒絕 “give up” 他們的匹配,即使這會危及整體匹配。

/<.*><.+>/.match("<a><b>") #=> #<MatchData "<a><b>">
/<.*+><.+>/.match("<a><b>") #=> nil
/<.*><.++>/.match("<a><b>") #=> nil

捕獲

括號可用於 capturing 。由第 n 組括號括起來的文本隨後可以用 n 引用。在模式中使用 backreference \n(例如 \1 );在模式之外使用 MatchData[n] (例如 MatchData[1] )。

在此示例中,'at' 由第一組括號捕獲,然後用 \1 引用:

/[csh](..) [csh]\1 in/.match("The cat sat in the hat")
    #=> #<MatchData "cat sat in" 1:"at">

Regexp#match 返回一個 MatchData 對象,該對象通過其 [] 方法使捕獲的文本可用:

/[csh](..) [csh]\1 in/.match("The cat sat in the hat")[1] #=> 'at'

雖然 Ruby 支持任意數量的編號捕獲組,但使用 \n 反向引用語法僅支持組 1-9。

Ruby 還支持 \0 作為特殊的反向引用,它引用整個匹配的字符串。這也可以在 MatchData[0] 獲得。注意\0反向引用不能在正則表達式內部使用,因為反向引用隻能在捕獲組結束後使用,而\0反向引用使用整個匹配的隱式捕獲組。但是,您可以在進行替換時使用此反向引用:

"The cat sat in the hat".gsub(/[csh]at/, '\0s')
  # => "The cats sats in the hats"

命名捕獲

使用 (?< name >)(?' name ') 構造定義時,可以按名稱引用捕獲組。

/\$(?<dollars>\d+)\.(?<cents>\d+)/.match("$3.67")
    #=> #<MatchData "$3.67" dollars:"3" cents:"67">
/\$(?<dollars>\d+)\.(?<cents>\d+)/.match("$3.67")[:dollars] #=> "3"

命名組可以使用 \k< name > 進行反向引用,其中 name 是組名稱。

/(?<vowel>[aeiou]).\k<vowel>.\k<vowel>/.match('ototomy')
    #=> #<MatchData "ototo" vowel:"o">

注意:正則表達式不能同時使用命名反向引用和編號反向引用。此外,如果在正則表達式中使用命名捕獲,則用於分組的括號會導致未命名捕獲被視為非捕獲。

/(\w)(\w)/.match("ab").captures # => ["a", "b"]
/(\w)(\w)/.match("ab").named_captures # => {}

/(?<c>\w)(\w)/.match("ab").captures # => ["a"]
/(?<c>\w)(\w)/.match("ab").named_captures # => {"c"=>"a"}

當命名捕獲組與表達式左側的文字正則表達式和 =~ 運算符一起使用時,捕獲的文本也會分配給具有相應名稱的局部變量。

/\$(?<dollars>\d+)\.(?<cents>\d+)/ =~ "$3.67" #=> 0
dollars #=> "3"

分組

括號還包括group 它們所包含的術語,允許將它們量化為一個整體atomic

下麵的模式匹配一個元音後跟 2 個單詞字符:

/[aeiou]\w{2}/.match("Caenorhabditis elegans") #=> #<MatchData "aen">

而下麵的模式匹配一個元音後跟一個單詞字符,兩次,即[aeiou]\w[aeiou]\w:‘enor’。

/([aeiou]\w){2}/.match("Caenorhabditis elegans")
    #=> #<MatchData "enor" 1:"or">

(?:) 構造提供分組而不捕獲。也就是說,它將它包含的術語組合成一個原子整體,而不創建反向引用。這以犧牲可讀性為代價提高了性能。

第一組括號捕獲‘n’ 和第二個‘ti’。第二組稍後使用反向引用 \2 引用:

/I(n)ves(ti)ga\2ons/.match("Investigations")
    #=> #<MatchData "Investigations" 1:"n" 2:"ti">

第一組括號現在用'?:'設為非捕獲,所以它仍然匹配‘n’,但不創建反向引用。因此,反向引用\1 現在引用‘ti’。

/I(?:n)ves(ti)ga\1ons/.match("Investigations")
    #=> #<MatchData "Investigations" 1:"ti">

原子分組

可以使用 (?> pat ) 進行分組 atomic 。這會導致子表達式pat 獨立於表達式的其餘部分進行匹配,以便它匹配的內容對於匹配的其餘部分變得固定,除非必須放棄整個子表達式並隨後重新訪問。這樣,pat 被視為不可分割的整體。原子分組通常用於優化模式,以防止正則表達式引擎不必要地回溯。

下麵模式中的 " 匹配字符串的第一個字符,然後 .* 匹配 Quote“ 。這會導致整體匹配失敗,因此 .* 匹配的文本會回溯一個位置,從而使字符串的最後一個字符可用於匹配 "

/".*"/.match('"Quote"')     #=> #<MatchData "\"Quote\"">

如果 .* 被原子分組,它拒絕回溯 Quote“ ,即使這意味著整體匹配失敗

/"(?>.*)"/.match('"Quote"') #=> nil

子表達式調用

\g< name > 語法再次匹配名為 name 的前一個子表達式,它可以是組名或編號。這與反向引用的不同之處在於它重新執行組,而不是簡單地嘗試重新匹配相同的文本。

此模式匹配 ( 字符並將其分配給 paren 組,嘗試再次調用 paren sub-expression 但失敗,然後匹配文字 )

/\A(?<paren>\(\g<paren>*\))*\z/ =~ '()'

/\A(?<paren>\(\g<paren>*\))*\z/ =~ '(())' #=> 0
# ^1
#      ^2
#           ^3
#                 ^4
#      ^5
#           ^6
#                      ^7
#                       ^8
#                       ^9
#                           ^10
  1. 匹配字符串的開頭,即第一個字符之前。

  2. 進入一個名為 paren 的命名捕獲組

  3. 匹配文字 ( ,即字符串中的第一個字符

  4. 再次調用paren組,即遞歸回到第二步

  5. 重新進入paren

  6. 匹配文字 ( ,即字符串中的第二個字符

  7. 嘗試第三次調用paren,但失敗,因為這樣做會阻止整體成功匹配

  8. 匹配文字 ) ,即字符串中的第三個字符。標記第二個遞歸調用的結束

  9. 匹配文字 ) ,即字符串中的第四個字符

  10. 匹配字符串的結尾

交替

豎線元字符 (|) 將多個表達式組合成一個匹配任何表達式的表達式。每個表達式都是一個 alternative

/\w(and|or)\w/.match("Feliformia") #=> #<MatchData "form" 1:"or">
/\w(and|or)\w/.match("furandi")    #=> #<MatchData "randi" 1:"and">
/\w(and|or)\w/.match("dissemblance") #=> nil

字符屬性

\p{} 構造將字符與命名屬性匹配,很像 POSIX 括號類。

  • /\p{Alnum}/ - 字母和數字字符

  • /\p{Alpha}/ - 字母字符

  • /\p{Blank}/ - 空格或製表符

  • /\p{Cntrl}/ - 控製字符

  • /\p{Digit}/ - 數字

  • /\p{Graph}/ - 非空白字符(不包括空格、控製字符和類似字符)

  • /\p{Lower}/ - 小寫字母字符

  • /\p{Print}/ - 與 \p{Graph} 類似,但包含空格字符

  • /\p{Punct}/ - 標點符號

  • /\p{Space}/ - 空白字符([:blank:]、換行符、回車符等)

  • /\p{Upper}/ - 大寫字母

  • /\p{XDigit}/ - 十六進製數字中允許的數字(即 0-9a-fA-F)

  • /\p{Word}/ - 以下 Unicode 通用類別之一的成員 LetterMarkNumberConnector_Punctuation

  • /\p{ASCII}/ - ASCII 字符集中的一個字符

  • /\p{Any}/ - 任何 Unicode 字符(包括未分配的字符)

  • /\p{Assigned}/ - 指定的字符

Unicode 字符的 General Category 值也可以與 \p{ Ab } 匹配,其中 Ab 是類別的縮寫,如下所述:

  • /\p{L}/ - “信”

  • /\p{Ll}/ - “字母:小寫”

  • /\p{Lm}/ - “字母:馬克”

  • /\p{Lo}/ - “信:其他”

  • /\p{Lt}/ - “字母:標題”

  • /\p{Lu}/ - '字母:大寫

  • /\p{Lo}/ - “信:其他”

  • /\p{M}/ - “標記”

  • /\p{Mn}/ - “標記:無間距”

  • /\p{Mc}/ - “標記:間距組合”

  • /\p{Me}/ - “標記:封閉”

  • /\p{N}/ - “數字”

  • /\p{Nd}/ - “數字:十進製數字”

  • /\p{Nl}/ - “數字:字母”

  • /\p{No}/ - “編號:其他”

  • /\p{P}/ - “標點符號”

  • /\p{Pc}/ - “標點符號:連接符”

  • /\p{Pd}/ - “標點符號:破折號”

  • /\p{Ps}/ - “標點符號:打開”

  • /\p{Pe}/ - “標點符號:關閉”

  • /\p{Pi}/ - “標點符號:初始引用”

  • /\p{Pf}/ - “標點符號:最後引述”

  • /\p{Po}/ - “標點符號:其他”

  • /\p{S}/ - “符號”

  • /\p{Sm}/ - “符號:數學”

  • /\p{Sc}/ - “符號:貨幣”

  • /\p{Sc}/ - “符號:貨幣”

  • /\p{Sk}/ - “符號:修飾符”

  • /\p{So}/ - “符號:其他”

  • /\p{Z}/ - “分隔符”

  • /\p{Zs}/ - “分隔符:空格”

  • /\p{Zl}/ - “分隔符:線”

  • /\p{Zp}/ - “分隔符:段落”

  • /\p{C}/ - “其他”

  • /\p{Cc}/ - “其他:控製”

  • /\p{Cf}/ - “其他:格式”

  • /\p{Cn}/ - “其他:未分配”

  • /\p{Co}/ - “其他:私人使用”

  • /\p{Cs}/ - “其他:代理”

最後,\p{}匹配字符的 Unicodescript.支持以下腳本:Arabic,Armenian,Balinese,Bengali,Bopomofo,Braille,Buginese,Buhid,Canadian_Aboriginal,Carian,Cham,Cherokee,Common,Coptic,Cuneiform,Cypriot,Cyrillic,Deseret,Devanagari,Ethiopic,Georgian,Glagolitic,Gothic,Greek,Gujarati,Gurmukhi,Han,Hangul,Hanunoo,Hebrew,Hiragana,Inherited,Kannada,Katakana,Kayah_Li,Kharoshthi,Khmer,Lao,Latin,Lepcha,Limbu,Linear_B,Lycian,Lydian,Malayalam,Mongolian,Myanmar,New_Tai_Lue,Nko,Ogham,Ol_Chiki,Old_Italic,Old_Persian,Oriya,Osmanya,Phags_Pa,Phoenician,Rejang,Runic,Saurashtra,Shavian,Sinhala,Sundanese,Syloti_Nagri,Syriac,Tagalog,Tagbanwa,Tai_Le,Tamil,Telugu,Thaana,Thai,Tibetan,Tifinagh,Ugaritic,Vai, 和Yi.

Unicode 代碼點 U+06E9 被命名為“ARABIC PLACE OF SAJDAH”,屬於阿拉伯文字:

/\p{Arabic}/.match("\u06E9") #=> #<MatchData "\u06E9">

所有字符屬性都可以通過在其名稱前加上插入符號 (^) 來反轉。

字母“A”不在 Unicode Ll(字母;小寫)類別中,因此該匹配成功:

/\p{^Ll}/.match("A") #=> #<MatchData "A">

錨點

錨點是匹配字符之間的zero-width 位置的元字符,anchoring 匹配特定位置的元字符。

  • ^ - 匹配行首

  • $ - 匹配行尾

  • \A - 匹配字符串的開頭。

  • \Z - 匹配字符串結尾。如果字符串以換行符結尾,則在換行符之前匹配

  • \z - 匹配字符串結尾

  • \G - 匹配第一個匹配位置:

    String#gsubString#scan 等方法中,它會在每次迭代時發生變化。它最初匹配主題的開頭,並且在隨後的每次迭代中,它匹配最後一次匹配完成的位置。

    "    a b c".gsub(/ /, '_')    #=> "____a_b_c"
    "    a b c".gsub(/\G /, '_')  #=> "____a b c"

    在像 Regexp#matchString#match 這樣采用(可選)偏移量的方法中,它匹配搜索開始的位置。

    "hello, world".match(/,/, 3)    #=> #<MatchData ",">
    "hello, world".match(/\G,/, 3)  #=> nil
  • \b - 在括號外匹配單詞邊界;括號內的退格 (0x08)

  • \B - 匹配非單詞邊界

  • (?= pat ) - Positive lookahead 斷言:確保以下字符匹配 pat ,但不包括匹配文本中的這些字符

  • (?! pat ) - Negative lookahead 斷言:確保以下字符不匹配 pat ,但不包括匹配文本中的這些字符

  • (?<= pat ) - Positive lookbehind 斷言:確保前麵的字符匹配 pat ,但不包括匹配文本中的這些字符

  • (?<! pat ) - Negative lookbehind 斷言:確保前麵的字符不匹配 pat ,但在匹配的文本中不包含這些字符

  • \K - 在正則表達式中使用 \K 之前的內容的正向後視。例如,以下兩個正則表達式幾乎是等價的:

    /ab\Kc/
    /(?<=ab)c/

    以下兩個正則表達式也是如此:

    /(a)\K(b)\Kc/
    /(?<=(?<=(a))(b))c/

如果模式沒有錨定,它可以從字符串中的任何點開始:

/real/.match("surrealist") #=> #<MatchData "real">

將模式錨定到字符串的開頭會強製匹配從那裏開始。 ‘real’ 沒有出現在字符串的開頭,所以現在匹配失敗:

/\Areal/.match("surrealist") #=> nil

下麵的匹配失敗,因為盡管“Demand”包含‘and’,但該模式不會出現在單詞邊界處。

/\band/.match("Demand")

而在以下示例中,‘and’ 已錨定到非單詞邊界,因此它不是匹配第一個 ‘and’,而是匹配 ‘demand’ 的第四個字母:

/\Band.+/.match("Supply and demand curve") #=> #<MatchData "and curve">

下麵的模式使用正向前瞻和正向後視來匹配出現在標簽中的文本,而不包括匹配中的標簽:

/(?<=<b>)\w+(?=<\/b>)/.match("Fortune favours the <b>bold</b>")
    #=> #<MatchData "bold">

選項

正則表達式的結束分隔符後麵可以跟一個或多個 single-letter 選項,這些選項控製模式如何匹配。

  • /pat/i - 忽略大小寫

  • /pat/m - 將換行符視為與 . 匹配的字符

  • /pat/x - 忽略模式中的空格和注釋

  • /pat/o - 僅執行一次 #{} 插值

imx 也可以通過 (? on - off ) 構造應用於子表達式級別,該構造啟用選項 on 並禁用選項 off 用於括號括起來的表達式:

/a(?i:b)c/.match('aBc')   #=> #<MatchData "aBc">
/a(?-i:b)c/i.match('ABC') #=> nil

此外,還可以為模式的其餘部分切換這些選項:

/a(?i)bc/.match('abC') #=> #<MatchData "abC">

選項也可以與 Regexp.new 一起使用:

Regexp.new("abc", Regexp::IGNORECASE)                     #=> /abc/i
Regexp.new("abc", Regexp::MULTILINE)                      #=> /abc/m
Regexp.new("abc # Comment", Regexp::EXTENDED)             #=> /abc # Comment/x
Regexp.new("abc", Regexp::IGNORECASE | Regexp::MULTILINE) #=> /abc/mi

Free-Spacing 模式和評論

如上所述,x 選項啟用free-spacing 模式。模式內的文字空白將被忽略,並且 octothorpe (#) 字符引入了注釋,直到行尾。這允許以可能更易讀的方式組織模式的組件。

一個人為的模式來匹配帶有可選小數位的數字:

float_pat = /\A
    [[:digit:]]+ # 1 or more digits before the decimal point
    (\.          # Decimal point
        [[:digit:]]+ # 1 or more digits after the decimal point
    )? # The decimal point and following digits are optional
\Z/x
float_pat.match('3.14') #=> #<MatchData "3.14" 1:".14">

有許多匹配空格的策略:

  • 使用 \s\p{Space} 等模式。

  • 使用轉義的空格,例如 \ ,即前麵有反斜杠的空格。

  • 使用字符類,例如 [ ]

注釋可以包含在具有 (?# comment ) 構造的非 x 模式中,其中 comment 是正則表達式引擎忽略的任意文本。

正則表達式文字中的注釋不能包含未轉義的終止符。

Encoding

假設正則表達式使用源編碼。這可以用以下修飾符之一覆蓋。

  • / pat /u - UTF-8

  • / pat /e - EUC-JP

  • / pat /s - Windows-31J

  • / pat /n - ASCII-8BIT

當一個正則表達式共享一個編碼,或者正則表達式的編碼是US-ASCII並且字符串的編碼是ASCII-compatible時,一個正則表達式可以與一個字符串匹配。

如果嘗試匹配不兼容的編碼,則會引發 Encoding::CompatibilityError 異常。

Regexp#fixed_encoding? 謂詞指示正則表達式是否具有 fixed 編碼,即與 ASCII 不兼容的編碼。正則表達式的編碼可以通過提供 Regexp::FIXEDENCODING 作為 Regexp.new 的第二個參數來顯式修複:

r = Regexp.new("a".force_encoding("iso-8859-1"),Regexp::FIXEDENCODING)
r =~ "a\u3042"
   # raises Encoding::CompatibilityError: incompatible encoding regexp match
   #         (ISO-8859-1 regexp with UTF-8 string)

特殊的全局變量

模式匹配設置一些全局變量:

  • $~ 等價於 Regexp.last_match

  • $& 包含完整匹配的文本;

  • $` 包含匹配前的字符串;

  • $' 包含匹配後的字符串;

  • $1$2等包含文本匹配第一、第二等捕獲組;

  • $+ 包含最後一個捕獲組。

例子:

m = /s(\w{2}).*(c)/.match('haystack') #=> #<MatchData "stac" 1:"ta" 2:"c">
$~                                    #=> #<MatchData "stac" 1:"ta" 2:"c">
Regexp.last_match                     #=> #<MatchData "stac" 1:"ta" 2:"c">

$&      #=> "stac"
        # same as m[0]
$`      #=> "hay"
        # same as m.pre_match
$'      #=> "k"
        # same as m.post_match
$1      #=> "ta"
        # same as m[1]
$2      #=> "c"
        # same as m[2]
$3      #=> nil
        # no third group in pattern
$+      #=> "c"
        # same as m[-1]

這些全局變量是線程局部變量和method-local 變量。

性能

某些結構的病態組合會導致性能極差。

考慮一個由 25 個 a 、一個 d 、4 個 a 和一個 c 組成的字符串。

s = 'a' * 25 + 'd' + 'a' * 4 + 'c'
#=> "aaaaaaaaaaaaaaaaaaaaaaaaadaaaac"

如您所料,以下模式會立即匹配:

/(b|a)/ =~ s #=> 0
/(b|a+)/ =~ s #=> 0
/(b|a+)*/ =~ s #=> 0

但是,以下模式需要更長的時間:

/(b|a+)*c/ =~ s #=> 26

發生這種情況是因為正則表達式中的原子被立即數 + 和封閉的 * 量化,沒有什麽可以區分哪個控製任何特定字符。產生的不確定性產生super-linear 性能。 (請參閱 Mastering Regular Expressions(第 3 版),第 222 頁,Jeffery Friedl 進行 in-depth 分析)。這種特殊情況可以通過使用原子分組來修複,從而防止不必要的回溯:

(start = Time.now) && /(b|a+)*c/ =~ s && (Time.now - start)
   #=> 24.702736882
(start = Time.now) && /(?>b|a+)*c/ =~ s && (Time.now - start)
   #=> 0.000166571

以下示例代表了類似的情況,對我來說執行大約需要 60 秒:

將 29 個 a 的字符串與 29 個可選的 a 後跟 29 個強製的 a 的模式匹配:

Regexp.new('a?' * 29 + 'a' * 29) =~ 'a' * 29

29 個可選的 a 匹配字符串,但這會阻止後麵的 29 個強製 a 匹配。然後 Ruby 必須反複回溯,以便在仍然匹配強製 29 的同時盡可能多地滿足可選匹配。很明顯,沒有一個可選匹配可以成功,但不幸的是,Ruby 忽略了這一事實。

提高性能的最佳方法是顯著減少所需的回溯量。對於這種情況,不是單獨匹配 29 個可選的 a s,而是可以使用 a{0,29} 一次匹配一係列可選的 a s:

Regexp.new('a{0,29}' + 'a' * 29) =~ 'a' * 29

相關用法


注:本文由純淨天空篩選整理自ruby-lang.org大神的英文原創作品 Regexp類。非經特殊聲明,原始代碼版權歸原作者所有,本譯文未經允許或授權,請勿轉載或複製。