本文簡要介紹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
方法
/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]/
表示 a
或 b
,而不是 /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 通用類別之一中的字符Letter
、Mark
、Number
、Connector_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
-
匹配字符串的開頭,即第一個字符之前。
-
進入一個名為
paren
的命名捕獲組 -
匹配文字
(
,即字符串中的第一個字符 -
再次調用
paren
組,即遞歸回到第二步 -
重新進入
paren
組 -
匹配文字
(
,即字符串中的第二個字符 -
嘗試第三次調用
paren
,但失敗,因為這樣做會阻止整體成功匹配 -
匹配文字
)
,即字符串中的第三個字符。標記第二個遞歸調用的結束 -
匹配文字
)
,即字符串中的第四個字符 -
匹配字符串的結尾
交替
豎線元字符 (|
) 將多個表達式組合成一個匹配任何表達式的表達式。每個表達式都是一個 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 通用類別之一的成員Letter
、Mark
、Number
、Connector_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#gsub
和String#scan
等方法中,它會在每次迭代時發生變化。它最初匹配主題的開頭,並且在隨後的每次迭代中,它匹配最後一次匹配完成的位置。" a b c".gsub(/ /, '_') #=> "____a_b_c" " a b c".gsub(/\G /, '_') #=> "____a b c"
在像
Regexp#match
和String#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
- 僅執行一次#{}
插值
i
、 m
和 x
也可以通過 (?
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 Regexp named_captures()用法及代碼示例
- Ruby Regexp to_s()用法及代碼示例
- Ruby Regexp.eql?用法及代碼示例
- Ruby Regexp hash()用法及代碼示例
- Ruby Regexp.fixed_encoding?用法及代碼示例
- Ruby Regexp.options用法及代碼示例
- Ruby Regexp inspect()用法及代碼示例
- Ruby Regexp.inspect用法及代碼示例
- Ruby Regexp.names用法及代碼示例
- Ruby Regexp source()用法及代碼示例
- Ruby Regexp match()用法及代碼示例
- Ruby Regexp.rxp =~ str用法及代碼示例
- Ruby Regexp.escape用法及代碼示例
- Ruby Regexp.casefold?用法及代碼示例
- Ruby Regexp.match用法及代碼示例
- Ruby Regexp.try_convert用法及代碼示例
- Ruby Regexp encoding()用法及代碼示例
- Ruby Regexp fixed_encoding?()用法及代碼示例
- Ruby Regexp escape()用法及代碼示例
- Ruby Regexp names()用法及代碼示例
- Ruby Regexp new()用法及代碼示例
- Ruby Regexp.rxp ===用法及代碼示例
- Ruby Regexp.match?用法及代碼示例
- Ruby Regexp quote()用法及代碼示例
- Ruby Regexp.union用法及代碼示例
注:本文由純淨天空篩選整理自ruby-lang.org大神的英文原創作品 Regexp類。非經特殊聲明,原始代碼版權歸原作者所有,本譯文未經允許或授權,請勿轉載或複製。