類別 Regexp

一個 正規表示式(也稱為 regexp)是一個 比對模式(也稱為 模式)。

正規表示式的一般表示法使用斜線字元包住

/foo/

正規表示式可以套用在一個 目標字串;字串中(如果有)與模式比對的部分稱為 比對,並且可以說 比對到

re = /red/
re.match?('redirect') # => true   # Match at beginning of target.
re.match?('bored')    # => true   # Match at end of target.
re.match?('credit')   # => true   # Match within target.
re.match?('foo')      # => false  # No match.

Regexp 用法

正規表示式可以用於

正規表示法物件

正規表示法物件具有

建立正規表示法

可以使用下列方式建立正規表示法

方法 match

每個方法 Regexp#matchString#matchSymbol#match 如果找到符合項,則會傳回 MatchData 物件,否則傳回 nil;每個方法也會設定 全域變數

'food'.match(/foo/) # => #<MatchData "foo">
'food'.match(/bar/) # => nil

運算子 =~

每個運算子 Regexp#=~String#=~Symbol#=~ 如果找到符合項,則會傳回整數偏移量,否則傳回 nil;每個方法也會設定 全域變數

/bar/ =~ 'foo bar' # => 4
'foo bar' =~ /bar/ # => 4
/baz/ =~ 'foo bar' # => nil

方法 match?

每個方法 Regexp#match?String#match?Symbol#match? 如果找到符合項,則會傳回 true,否則傳回 false;沒有任何方法會設定 全域變數

'food'.match?(/foo/) # => true
'food'.match?(/bar/) # => false

全域變數

某些以正規表示法為導向的方法會將值指定給全域變數

受影響的全局變數為

範例

# Matched string, but no matched groups.
'foo bar bar baz'.match('bar')
$~ # => #<MatchData "bar">
$& # => "bar"
$` # => "foo "
$' # => " bar baz"
$+ # => nil
$1 # => nil

# Matched groups.
/s(\w{2}).*(c)/.match('haystack')
$~ # => #<MatchData "stac" 1:"ta" 2:"c">
$& # => "stac"
$` # => "hay"
$' # => "k"
$+ # => "c"
$1 # => "ta"
$2 # => "c"
$3 # => nil

# No match.
'foo'.match('bar')
$~ # => nil
$& # => nil
$` # => nil
$' # => nil
$+ # => nil
$1 # => nil

請注意,Regexp#match?String#match?Symbol#match? 不會設定全局變數。

來源

如上所示,最簡單的正規表示法使用文字表達式作為其來源

re = /foo/              # => /foo/
re.match('food')        # => #<MatchData "foo">
re.match('good')        # => nil

豐富的可用子表達式集合賦予正規表示法強大的功能和彈性

特殊字元

正規表示法特殊字元,稱為元字元,在特定脈絡中具有特殊意義;根據脈絡,這些有時是元字元

. ? - + * ^ \ | $ ( ) [ ] { }

若要以文字方式相符元字元,請使用反斜線跳脫

# Matches one or more 'o' characters.
/o+/.match('foo')  # => #<MatchData "oo">
# Would match 'o+'.
/o\+/.match('foo') # => nil

若要以文字方式相符反斜線,請使用反斜線跳脫

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

方法 Regexp.escape 傳回跳脫字串

Regexp.escape('.?-+*^\|$()[]{}')
# => "\\.\\?\\-\\+\\*\\^\\\\\\|\\$\\(\\)\\[\\]\\{\\}"

來源文字

來源文字在很大程度上表現得像雙引號字串;請參閱 字串文字

特別是,來源文字可能包含內插表達式

s = 'foo'         # => "foo"
/#{s}/            # => /foo/
/#{s.capitalize}/ # => /Foo/
/#{2 + 2}/        # => /4/

一般字串文字和來源文字之間有差異;請參閱 簡寫字元類別

字元類別

字元類別以方括弧分隔;它指定某些字元在目標字串的特定點相符

# This character class will match any vowel.
re = /B[aeiou]rd/
re.match('Bird') # => #<MatchData "Bird">
re.match('Bard') # => #<MatchData "Bard">
re.match('Byrd') # => nil

字元類別可以包含連字元號來指定字元範圍

# These regexps have the same effect.
/[abcdef]/.match('foo') # => #<MatchData "f">
/[a-f]/.match('foo')    # => #<MatchData "f">
/[a-cd-f]/.match('foo') # => #<MatchData "f">

當字元類別的第一個字元是插入符號 (^) 時,類別的意義會反轉:它會比對任何字元,除了指定字元之外。

/[^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]/

簡寫字元類別

下列每個元字元都作為字元類別的簡寫

錨點

錨點是元序列,它比對目標字串中字元之間的零寬度位置。

對於沒有錨點的子表達式,比對可以從目標字串中的任何位置開始

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

對於有錨點的子表達式,比對必須從比對的錨點開始。

邊界錨點

這些錨點每個都比對一個邊界

環顧錨點

前瞻錨點

後顧錨點

下方的範例使用正前瞻和正後顧來比對出現在 標籤中的文字,但不包含比對中的標籤

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

比對重設錨點

交替

垂直線元字元 (|) 可用於括弧內表示交替:兩個或多個子表示式,其中任何一個都可能比對目標字串。

兩個交替

re = /(a|b)/
re.match('foo') # => nil
re.match('bar') # => #<MatchData "b" 1:"b">

四個交替

re = /(a|b|c|d)/
re.match('shazam') # => #<MatchData "a" 1:"a">
re.match('cold')   # => #<MatchData "c" 1:"c">

每個交替都是一個子表示式,且可能由其他子表示式組成

re = /([a-c]|[x-z])/
re.match('bar') # => #<MatchData "b" 1:"b">
re.match('ooz') # => #<MatchData "z" 1:"z">

方法 Regexp.union 提供一個方便的方法來建構具有交替的正則表示法。

量詞

一個簡單的正則表示法比對一個字元

/\w/.match('Hello')  # => #<MatchData "H">

新增的量詞指定需要或允許多少比對

貪婪、懶惰或獨佔匹配

量詞匹配可能是貪婪、懶惰或獨佔

更多

群組和擷取

一個簡單的正規表示式有(最多)一個匹配項

re = /\d\d\d\d-\d\d-\d\d/
re.match('1943-02-04')      # => #<MatchData "1943-02-04">
re.match('1943-02-04').size # => 1
re.match('foo')             # => nil

新增一對或多對括號 (子表示式),定義群組,這可能會產生多個匹配的子字串,稱為擷取

re = /(\d\d\d\d)-(\d\d)-(\d\d)/
re.match('1943-02-04')      # => #<MatchData "1943-02-04" 1:"1943" 2:"02" 3:"04">
re.match('1943-02-04').size # => 4

第一個擷取是整個匹配的字串;其他擷取是來自群組的匹配子字串。

一個群組可能有一個 量詞

re = /July 4(th)?/
re.match('July 4')   # => #<MatchData "July 4" 1:nil>
re.match('July 4th') # => #<MatchData "July 4th" 1:"th">

re = /(foo)*/
re.match('')       # => #<MatchData "" 1:nil>
re.match('foo')    # => #<MatchData "foo" 1:"foo">
re.match('foofoo') # => #<MatchData "foofoo" 1:"foo">

re = /(foo)+/
re.match('')       # => nil
re.match('foo')    # => #<MatchData "foo" 1:"foo">
re.match('foofoo') # => #<MatchData "foofoo" 1:"foo">

回傳的 MatchData 物件提供對匹配子字串的存取

re = /(\d\d\d\d)-(\d\d)-(\d\d)/
md = re.match('1943-02-04')
# => #<MatchData "1943-02-04" 1:"1943" 2:"02" 3:"04">
md[0] # => "1943-02-04"
md[1] # => "1943"
md[2] # => "02"
md[3] # => "04"

非擷取群組

群組可以設為非擷取;它仍然是一個群組(例如,可以有一個量詞),但其配對的子字串不會包含在擷取中。

非擷取群組以 ?: 開頭(在括號內)

# Don't capture the year.
re = /(?:\d\d\d\d)-(\d\d)-(\d\d)/
md = re.match('1943-02-04') # => #<MatchData "1943-02-04" 1:"02" 2:"04">

反向參照

群組配對也可以在正規表示法本身中參照;這樣的參照稱為反向參照

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

此表格顯示正規表示法中每個子表示式如何配對目標字串中的子字串

| Subexpression in Regexp   | Matching Substring in Target String |
|---------------------------|-------------------------------------|
|       First '[csh]'       |            Character 'c'            |
|          '(..)'           |        First substring 'at'         |
|      First space ' '      |      First space character ' '      |
|       Second '[csh]'      |            Character 's'            |
| '\1' (backreference 'at') |        Second substring 'at'        |
|           ' in'           |            Substring ' in'          |

正規表示法可以包含任意數量的群組

命名擷取

如上所述,可以透過其數字參照擷取。擷取也可以有一個名稱,其前綴為 ?<name>?'name',名稱(符號)可以用作 MatchData[] 中的索引

md = /\$(?<dollars>\d+)\.(?'cents'\d+)/.match("$3.67")
# => #<MatchData "$3.67" dollars:"3" cents:"67">
md[:dollars]  # => "3"
md[:cents]    # => "67"
# The capture numbers are still valid.
md[2]         # => "67"

當正規表示法包含命名擷取時,沒有未命名擷取

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

命名群組可以反向參照為 \k<name>

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

當(且僅當)正規表示法包含命名擷取群組並出現在 =~ 算子之前時,擷取的子字串會指派給具有對應名稱的局部變數

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

方法 Regexp#named_captures 傳回擷取名稱和子字串的雜湊;方法 Regexp#names 傳回擷取名稱的陣列。

原子群組

群組可以使用 (?>子表示式) 設為原子

這會導致子表示式獨立於表示式的其餘部分進行配對,以便配對的子字串在配對的其餘部分中固定,除非必須放棄整個子表示式並隨後重新檢視。

這樣,子表示式被視為不可分割的整體。原子群組通常用於最佳化模式,以防止不必要的回溯。

範例(沒有原子群組)

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

分析

  1. 模式中開頭的子表達式 " 與目標字串中的第一個字元 " 相符。

  2. 下一個子表達式 .* 與下一個子字串 Quote“ 相符(包含尾隨的雙引號)。

  3. 現在目標字串中沒有任何內容與模式中尾隨的子表達式 " 相符;這會導致整體比對失敗。

  4. 比對的子字串回溯一個位置:Quote

  5. 最後一個子表達式 " 現在與最後一個子字串 " 相符,整體比對成功。

如果子表達式 .* 是原子性分組,回溯會被停用,整體比對會失敗

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

原子性分組會影響效能;請參閱 原子性分組

子表達式呼叫

如上所示,回溯參考號碼 (\n) 或名稱 (\k<name>) 可存取擷取的子字串;也可以透過號碼 (\gn) 或名稱 (\g<name>) 存取對應的 regexp 子表達式

/\A(?<paren>\(\g<paren>*\))*\z/.match('(())')
# ^1
#      ^2
#           ^3
#                 ^4
#      ^5
#           ^6
#                      ^7
#                       ^8
#                       ^9
#                           ^10

模式

  1. 與字串開頭相符,也就是第一個字元之前。

  2. 進入命名分組 paren

  3. 與字串中的第一個字元 '(' 相符。

  4. 再次呼叫 paren 分組,也就是遞迴回到第二個步驟。

  5. 再次進入 paren 分組。

  6. 與字串中的第二個字元 '(' 相符。

  7. 嘗試第三次呼叫 paren,但失敗,因為這樣做會阻止整體比對成功。

  8. 與字串中的第三個字元 ')' 相符;標示第二次遞迴呼叫的結束

  9. 與字串中的第四個字元 ')' 相符。

  10. 與字串的結尾相符。

請參閱 子表達式呼叫

條件式

條件式結構採用 (?(cond)yes|no) 形式,其中

範例

re = /\A(foo)?(?(1)(T)|(F))\z/
re.match('fooT') # => #<MatchData "fooT" 1:"foo" 2:"T" 3:nil>
re.match('F')    # => #<MatchData "F" 1:nil 2:nil 3:"F">
re.match('fooF') # => nil
re.match('T')    # => nil

re = /\A(?<xyzzy>foo)?(?(<xyzzy>)(T)|(F))\z/
re.match('fooT') # => #<MatchData "fooT" xyzzy:"foo">
re.match('F')    # => #<MatchData "F" xyzzy:nil>
re.match('fooF') # => nil
re.match('T')    # => nil

不存在運算子

不存在運算子是一個特殊分組,與與包含的子表達式相符的任何內容相符。

/(?~real)/.match('surrealist') # => #<MatchData "surrea">
/(?~real)ist/.match('surrealist') # => #<MatchData "ealist">
/sur(?~real)ist/.match('surrealist') # => nil

Unicode

Unicode 屬性

/\p{property_name}/ 結構(小寫 p)使用 Unicode 屬性名稱與字元相符,很像字元類別;屬性 Alpha 指定字母字元

/\p{Alpha}/.match('a') # => #<MatchData "a">
/\p{Alpha}/.match('1') # => nil

屬性可以在名稱前加上插入符號 (^) 來反轉

/\p{^Alpha}/.match('1') # => #<MatchData "1">
/\p{^Alpha}/.match('a') # => nil

或使用 \P(大寫 P

/\P{Alpha}/.match('1') # => #<MatchData "1">
/\P{Alpha}/.match('a') # => nil

請參閱 Unicode 屬性,以取得基於許多屬性的正規表示法。

一些常用的屬性對應於 POSIX 方括號表示法

以下也常使用

Unicode 字元類別

Unicode 字元類別名稱

範例

/\p{lu}/                # => /\p{lu}/
/\p{LU}/                # => /\p{LU}/
/\p{Uppercase Letter}/  # => /\p{Uppercase Letter}/
/\p{Uppercase_Letter}/  # => /\p{Uppercase_Letter}/
/\p{UPPERCASE-LETTER}/  # => /\p{UPPERCASE-LETTER}/

以下是 Unicode 字元類別縮寫和名稱。每個類別中字元的列舉位於連結中。

字母

符號

數字

標點符號

Unicode 碼表和區塊

Unicode 屬性包括

POSIX 方括號表示法

POSIX 方括號表示法 也類似於字元類別。這些表示法提供上述內容的可攜式替代方案,並具有涵蓋非 ASCII 字元的額外好處

POSIX 方括號表示法

Ruby 也支援這些 (非 POSIX) 方括號表示式

註解

可以使用 (?#註解) 結構在正規表示式模式中加入註解,其中 註解 是要忽略的子字串。正規表示式引擎忽略的任意文字

/foo(?#Ignore me)bar/.match('foobar') # => #<MatchData "foobar">

註解不能包含未跳脫的終止字元。

另請參閱 延伸模式

模式

下列每個修飾詞都為正規表示式設定模式

可以套用其中任何一個、全部或一個都沒有。

修飾詞 imx 可以套用在子表示式

範例

re = /(?i)te(?-i)st/
re.match('test') # => #<MatchData "test">
re.match('TEst') # => #<MatchData "TEst">
re.match('TEST') # => nil
re.match('teST') # => nil

re = /t(?i:e)st/
re.match('test') # => #<MatchData "test">
re.match('tEst') # => #<MatchData "tEst">
re.match('tEST') # => nil

方法 Regexp#options 傳回一個整數,其值顯示不分大小寫模式、多行模式和延伸模式的設定。

不分大小寫模式

預設情況下,regexp 會區分大小寫

/foo/.match('FOO')  # => nil

修改器 i 可啟用不區分大小寫模式

/foo/i.match('FOO')
# => #<MatchData "FOO">

方法 Regexp#casefold? 會傳回模式是否不區分大小寫。

多行模式

Ruby 中的多行模式通常稱為「點全部模式」

與其他語言不同,修改器 m 不會影響錨定 ^$。這些錨定在 Ruby 中總是會比對行界線。

延伸模式

修改器 x 可啟用延伸模式,表示

在延伸模式中,空白和註解可用於形成自文件化的 regexp。

Regexp 不在延伸模式中(比對一些羅馬數字)

pattern = '^M{0,3}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$'
re = /#{pattern}/
re.match('MCMXLIII') # => #<MatchData "MCMXLIII" 1:"CM" 2:"XL" 3:"III">

Regexp 在延伸模式中

pattern = <<-EOT
  ^                   # beginning of string
  M{0,3}              # thousands - 0 to 3 Ms
  (CM|CD|D?C{0,3})    # hundreds - 900 (CM), 400 (CD), 0-300 (0 to 3 Cs),
                      #            or 500-800 (D, followed by 0 to 3 Cs)
  (XC|XL|L?X{0,3})    # tens - 90 (XC), 40 (XL), 0-30 (0 to 3 Xs),
                      #        or 50-80 (L, followed by 0 to 3 Xs)
  (IX|IV|V?I{0,3})    # ones - 9 (IX), 4 (IV), 0-3 (0 to 3 Is),
                      #        or 5-8 (V, followed by 0 to 3 Is)
  $                   # end of string
EOT
re = /#{pattern}/x
re.match('MCMXLIII') # => #<MatchData "MCMXLIII" 1:"CM" 2:"XL" 3:"III">

內插模式

修改器 o 表示第一次遇到帶有內插的字面 regexp 時,產生的 Regexp 物件會被儲存,並用於該字面 regexp 的所有未來評估。沒有修改器 o 時,產生的 Regexp 就不會被儲存,因此字面 regexp 的每次評估都會產生新的 Regexp 物件。

沒有修改器 o

def letters; sleep 5; /[A-Z][a-z]/; end
words = %w[abc def xyz]
start = Time.now
words.each {|word| word.match(/\A[#{letters}]+\z/) }
Time.now - start # => 15.0174892

有修改器 o

start = Time.now
words.each {|word| word.match(/\A[#{letters}]+\z/o) }
Time.now - start # => 5.0010866

請注意,如果字面 regexp 沒有內插,則 o 行為是預設值。

編碼

預設情況下,只包含 US-ASCII 字元的 regexp 會有 US-ASCII 編碼

re = /foo/
re.source.encoding # => #<Encoding:US-ASCII>
re.encoding        # => #<Encoding:US-ASCII>

包含非 US-ASCII 字元的正規表示法會假設使用來源編碼。可以用下列修改器之一覆寫此設定。

regexp 可以與目標字串比對,當

如果嘗試比對不相容的編碼,則會引發 Encoding::CompatibilityError 例外。

範例

re = eval("# encoding: ISO-8859-1\n/foo\\xff?/")
re.encoding                 # => #<Encoding:ISO-8859-1>
re =~ "foo".encode("UTF-8") # => 0
re =~ "foo\u0100"           # Raises Encoding::CompatibilityError

編碼可以透過在 Regexp.new 的第二個引數中包含 Regexp::FIXEDENCODING 來明確固定

# Regexp with encoding ISO-8859-1.
re = Regexp.new("a".force_encoding('iso-8859-1'), Regexp::FIXEDENCODING)
re.encoding  # => #<Encoding:ISO-8859-1>
# Target string with encoding UTF-8.
s = "a\u3042"
s.encoding   # => #<Encoding:UTF-8>
re.match(s)  # Raises Encoding::CompatibilityError.

逾時

當正規表示式來源或目標字串來自不可信的輸入時,惡意的值可能會成為拒絕服務攻擊;為防止此類攻擊,最好設定逾時。

正規表示式有兩個逾時值

當 regexp.timeout 為 nil 時,逾時會「傳遞」至 Regexp.timeout;當 regexp.timeout 為非 nil 時,該值會控制逾時

| regexp.timeout Value | Regexp.timeout Value |            Result           |
|----------------------|----------------------|-----------------------------|
|         nil          |          nil         |       Never times out.      |
|         nil          |         Float        | Times out in Float seconds. |
|        Float         |          Any         | Times out in Float seconds. |

最佳化

對於模式和目標字串的某些值,比對時間可能會以多項式或指數方式隨著輸入大小而增加;由此產生的潛在漏洞是 正規表示式拒絕服務 (ReDoS) 攻擊。

正規表示式比對可以套用最佳化來防止 ReDoS 攻擊。套用最佳化後,比對時間會以線性方式(而非多項式或指數方式)隨著輸入大小而增加,且無法進行 ReDoS 攻擊。

如果模式符合下列條件,則會套用此最佳化

您可以使用 Regexp.linear_time? 方法來判斷模式是否符合這些條件

Regexp.linear_time?(/a*/)     # => true
Regexp.linear_time?('a*')     # => true
Regexp.linear_time?(/(a*)\1/) # => false

但是,即使該方法傳回 true,不可信的來源也可能不安全,因為最佳化會使用記憶化(可能會導致大量記憶體消耗)。

參考資料

閱讀(線上 PDF 書籍)

探索、測試(互動式線上編輯器)

常數

EXTENDED

請參閱 Regexp.optionsRegexp.new

FIXEDENCODING

請參閱 Regexp.optionsRegexp.new

IGNORECASE

請參閱 Regexp.optionsRegexp.new

MULTILINE

請參閱 Regexp.optionsRegexp.new

NOENCODING

請參閱 Regexp.optionsRegexp.new

公開類別方法

compile(*args)

別名為 Regexp.new

escape(字串) → 新字串 按一下以切換來源

傳回一個新的字串,其中會跳脫正則表達式中具有特殊意義的任何字元

s = Regexp.escape('\*?{}.')      # => "\\\\\\*\\?\\{\\}\\."

對於任何字串 s,此呼叫會傳回一個 MatchData 物件

r = Regexp.new(Regexp.escape(s)) # => /\\\\\\\*\\\?\\\{\\\}\\\./
r.match(s)                       # => #<MatchData "\\\\\\*\\?\\{\\}\\.">
static VALUE
rb_reg_s_quote(VALUE c, VALUE str)
{
    return rb_reg_quote(reg_operand(str, TRUE));
}
json_create(物件) 按一下以切換來源

請參閱 as_json

# File ext/json/lib/json/add/regexp.rb, line 9
def self.json_create(object)
  new(object['s'], object['o'])
end
last_match → 比對資料或 nil 按一下以切換來源
last_match(n) → 字串或 nil
last_match(名稱) → 字串或 nil

不帶任何引數時,傳回 $! 的值,這是最近模式比對的結果(請參閱 Regexp 全域變數

/c(.)t/ =~ 'cat'  # => 0
Regexp.last_match # => #<MatchData "cat" 1:"a">
/a/ =~ 'foo'      # => nil
Regexp.last_match # => nil

帶有非負整數引數 n 時,傳回比對資料中的第 _n_ 個欄位(如果有),否則傳回 nil

/c(.)t/ =~ 'cat'     # => 0
Regexp.last_match(0) # => "cat"
Regexp.last_match(1) # => "a"
Regexp.last_match(2) # => nil

帶有負整數引數 n 時,從最後一個欄位開始往回計算

Regexp.last_match(-1)       # => "a"

帶有字串或符號引數 name 時,傳回指定擷取的字串值(如果有)

/(?<lhs>\w+)\s*=\s*(?<rhs>\w+)/ =~ 'var = val'
Regexp.last_match        # => #<MatchData "var = val" lhs:"var"rhs:"val">
Regexp.last_match(:lhs)  # => "var"
Regexp.last_match('rhs') # => "val"
Regexp.last_match('foo') # Raises IndexError.
static VALUE
rb_reg_s_last_match(int argc, VALUE *argv, VALUE _)
{
    if (rb_check_arity(argc, 0, 1) == 1) {
        VALUE match = rb_backref_get();
        int n;
        if (NIL_P(match)) return Qnil;
        n = match_backref_number(match, argv[0]);
        return rb_reg_nth_match(n, match);
    }
    return match_getter();
}
linear_time?(re) 按一下以切換來源
linear_time?(字串, 選項 = 0)

如果針對 re 的比對可以在輸入字串中以線性時間執行,則傳回 true

Regexp.linear_time?(/re/) # => true

請注意,這是 ruby 解釋器的屬性,而不是引數正規表示式的屬性。相同的正規表示式可以或不能以線性時間執行,具體取決於您的 ruby 二進位檔。此方法的傳回值不保證向前或向後相容性。我們目前的演算法為 (*1),但這可能會在未來變更。其他實作也可能表現不同。它們可能會永遠對所有內容傳回 false。

(*1): doi.org/10.1109/SP40001.2021.00032

static VALUE
rb_reg_s_linear_time_p(int argc, VALUE *argv, VALUE self)
{
    struct reg_init_args args;
    VALUE re = reg_extract_args(argc, argv, &args);

    if (NIL_P(re)) {
        re = reg_init_args(rb_reg_alloc(), args.str, args.enc, args.flags);
    }

    return RBOOL(onig_check_linear_time(RREGEXP_PTR(re)));
}
new(字串, 選項 = 0, 超時: nil) → 正規表示式 按一下以切換來源
new(正規表示式, 超時: nil) → 正規表示式

如果給定引數 字串,則傳回一個包含給定字串和選項的新正規表示式

r = Regexp.new('foo') # => /foo/
r.source              # => "foo"
r.options             # => 0

選用引數 選項為下列其中之一

  • 一個包含選項的 字串

    Regexp.new('foo', 'i')  # => /foo/i
    Regexp.new('foo', 'im') # => /foo/im
    
  • 常數 Regexp::EXTENDEDRegexp::IGNORECASERegexp::MULTILINERegexp::NOENCODING 中一個或多個的按位元 OR

    Regexp.new('foo', Regexp::IGNORECASE) # => /foo/i
    Regexp.new('foo', Regexp::EXTENDED)   # => /foo/x
    Regexp.new('foo', Regexp::MULTILINE)  # => /foo/m
    Regexp.new('foo', Regexp::NOENCODING)  # => /foo/n
    flags = Regexp::IGNORECASE | Regexp::EXTENDED |  Regexp::MULTILINE
    Regexp.new('foo', flags)              # => /foo/mix
    
  • nilfalse,會略過。

  • 任何其他真值,正規表示式將不區分大小寫。

如果給定選用關鍵字引數 timeout,其浮點值會覆寫類別的超時間隔 Regexp.timeout。如果傳入 nil 作為 +timeout,它會使用類別的超時間隔 Regexp.timeout

如果給定引數 正規表示式,則傳回一個新的正規表示式。來源、選項和超時與 正規表示式 相同。選項n_flag 引數無效。超時可以被 timeout 關鍵字覆寫。

options = Regexp::MULTILINE
r = Regexp.new('foo', options, timeout: 1.1) # => /foo/m
r2 = Regexp.new(r)                           # => /foo/m
r2.timeout                                   # => 1.1
r3 = Regexp.new(r, timeout: 3.14)            # => /foo/m
r3.timeout                                   # => 3.14
static VALUE
rb_reg_initialize_m(int argc, VALUE *argv, VALUE self)
{
    struct reg_init_args args;
    VALUE re = reg_extract_args(argc, argv, &args);

    if (NIL_P(re)) {
        reg_init_args(self, args.str, args.enc, args.flags);
    }
    else {
        reg_copy(self, re);
    }

    set_timeout(&RREGEXP_PTR(self)->timelimit, args.timeout);

    return self;
}
escape(字串) → 新字串 按一下以切換來源

傳回一個新的字串,其中會跳脫正則表達式中具有特殊意義的任何字元

s = Regexp.escape('\*?{}.')      # => "\\\\\\*\\?\\{\\}\\."

對於任何字串 s,此呼叫會傳回一個 MatchData 物件

r = Regexp.new(Regexp.escape(s)) # => /\\\\\\\*\\\?\\\{\\\}\\\./
r.match(s)                       # => #<MatchData "\\\\\\*\\?\\{\\}\\.">
static VALUE
rb_reg_s_quote(VALUE c, VALUE str)
{
    return rb_reg_quote(reg_operand(str, TRUE));
}
timeout → 浮點數或 nil 按一下以切換來源

它傳回 Regexp 匹配的目前預設超時間隔(單位為秒)。nil 表示沒有預設超時組態。

static VALUE
rb_reg_s_timeout_get(VALUE dummy)
{
    double d = hrtime2double(rb_reg_match_time_limit);
    if (d == 0.0) return Qnil;
    return DBL2NUM(d);
}
timeout = 浮點數或 nil 按一下以切換來源

它設定 Regexp 匹配的預設超時間隔(單位為秒)。nil 表示沒有預設超時組態。此組態是處理序全域的。如果您要為每個 Regexp 設定超時,請對 Regexp.new 使用 timeout 關鍵字。

Regexp.timeout = 1
/^a*b?a*$/ =~ "a" * 100000 + "x" #=> regexp match timeout (RuntimeError)
static VALUE
rb_reg_s_timeout_set(VALUE dummy, VALUE timeout)
{
    rb_ractor_ensure_main_ractor("can not access Regexp.timeout from non-main Ractors");

    set_timeout(&rb_reg_match_time_limit, timeout);

    return timeout;
}
try_convert(物件) → 正規表示式或 nil 按一下以切換來源

如果 物件 是正規表示式,則傳回 物件

Regexp.try_convert(/re/) # => /re/

否則,如果 物件 對應到 :to_regexp,則呼叫 object.to_regexp 並傳回結果。

如果物件不回應 :to_regexp,則傳回 nil

Regexp.try_convert('re') # => nil

除非 object.to_regexp 傳回正規表示式,否則會引發例外狀況。

static VALUE
rb_reg_s_try_convert(VALUE dummy, VALUE re)
{
    return rb_check_regexp_type(re);
}
union(*patterns) → regexp 按一下以切換來源
union(array_of_patterns) → regexp

傳回一個新的正規表示式,為給定樣式的聯集

r = Regexp.union(%w[cat dog])      # => /cat|dog/
r.match('cat')      # => #<MatchData "cat">
r.match('dog')      # => #<MatchData "dog">
r.match('cog')      # => nil

對於每個字串樣式,會使用 Regexp.new(pattern)

Regexp.union('penzance')             # => /penzance/
Regexp.union('a+b*c')                # => /a\+b\*c/
Regexp.union('skiing', 'sledding')   # => /skiing|sledding/
Regexp.union(['skiing', 'sledding']) # => /skiing|sledding/

對於每個正規表示式樣式,會原樣使用,包括其旗標

Regexp.union(/foo/i, /bar/m, /baz/x)
# => /(?i-mx:foo)|(?m-ix:bar)|(?x-mi:baz)/
Regexp.union([/foo/i, /bar/m, /baz/x])
# => /(?i-mx:foo)|(?m-ix:bar)|(?x-mi:baz)/

如果沒有參數,則傳回 /(?!)/

Regexp.union # => /(?!)/

如果任何正規表示式樣式包含擷取,則行為未指定。

static VALUE
rb_reg_s_union_m(VALUE self, VALUE args)
{
    VALUE v;
    if (RARRAY_LEN(args) == 1 &&
        !NIL_P(v = rb_check_array_type(rb_ary_entry(args, 0)))) {
        return rb_reg_s_union(self, v);
    }
    return rb_reg_s_union(self, args);
}

公開實例方法

regexp == object → true 或 false

如果 object 是另一個 Regexp,其樣式、旗標和編碼與 self 相同,則傳回 true,否則傳回 false

/foo/ == Regexp.new('foo')                          # => true
/foo/ == /foo/i                                     # => false
/foo/ == Regexp.new('food')                         # => false
/foo/ == Regexp.new("abc".force_encoding("euc-jp")) # => false
別名:eql?
regexp === string → true 或 false 按一下以切換來源

如果 selfstring 中找到相符項目,則傳回 true

/^[a-z]*$/ === 'HELLO' # => false
/^[A-Z]*$/ === 'HELLO' # => true

此方法會在 case 陳述式中呼叫

s = 'HELLO'
case s
when /\A[a-z]*\z/; print "Lower case\n"
when /\A[A-Z]*\z/; print "Upper case\n"
else               print "Mixed case\n"
end # => "Upper case"
static VALUE
rb_reg_eqq(VALUE re, VALUE str)
{
    long start;

    str = reg_operand(str, FALSE);
    if (NIL_P(str)) {
        rb_backref_set(Qnil);
        return Qfalse;
    }
    start = rb_reg_search(re, str, 0, 0);
    return RBOOL(start >= 0);
}
regexp =~ string → 整數或 nil 按一下以切換來源

傳回 selfstring 的第一個相符項目的整數索引(以字元為單位),或如果沒有相符項目,則傳回 nil;還會設定 rdoc-ref:Regexp 全域變數

/at/ =~ 'input data' # => 7
$~                   # => #<MatchData "at">
/ax/ =~ 'input data' # => nil
$~                   # => nil

如果且僅當 self 是正規表示式文字時,將命名擷取指派給相同名稱的區域變數

  • 請參閱正規表示式文字。

  • 不包含內插;請參閱 正規表示式內插

  • 在表達式的左側。

範例

/(?<lhs>\w+)\s*=\s*(?<rhs>\w+)/ =~ '  x = y  '
p lhs # => "x"
p rhs # => "y"

如果未相符,則指派 nil

/(?<lhs>\w+)\s*=\s*(?<rhs>\w+)/ =~ '  x = '
p lhs # => nil
p rhs # => nil

如果 self 不是正規表示式文字,則不會進行區域變數指派

r = /(?<foo>\w+)\s*=\s*(?<foo>\w+)/
r =~ '  x = y  '
p foo # Undefined local variable
p bar # Undefined local variable

如果正規表示式不在左側,則不會進行指派

'  x = y  ' =~ /(?<foo>\w+)\s*=\s*(?<foo>\w+)/
p foo, foo # Undefined local variables

正規表示式內插 #{} 也會停用指派

r = /(?<foo>\w+)/
/(?<foo>\w+)\s*=\s*#{r}/ =~ 'x = y'
p foo # Undefined local variable
VALUE
rb_reg_match(VALUE re, VALUE str)
{
    long pos = reg_match_pos(re, &str, 0, NULL);
    if (pos < 0) return Qnil;
    pos = rb_str_sublen(str, pos);
    return LONG2FIX(pos);
}
as_json(*) 按一下以切換來源

方法 Regexp#as_jsonRegexp.json_create 可用於序列化和取消序列化 Regexp 物件;請參閱 Marshal

方法 Regexp#as_json 會序列化 self,傳回一個表示 self 的 2 元素雜湊

require 'json/add/regexp'
x = /foo/.as_json
# => {"json_class"=>"Regexp", "o"=>0, "s"=>"foo"}

方法 JSON.create 會將此類雜湊反序列化,傳回一個 Regexp 物件

Regexp.json_create(x) # => /foo/
# File ext/json/lib/json/add/regexp.rb, line 28
def as_json(*)
  {
    JSON.create_id => self.class.name,
    'o'            => options,
    's'            => source,
  }
end
casefold?→ true 或 false 按一下以切換來源

如果 self 中設定了不區分大小寫標記,則傳回 true,否則傳回 false

/a/.casefold?           # => false
/a/i.casefold?          # => true
/(?i:a)/.casefold?      # => false
static VALUE
rb_reg_casefold_p(VALUE re)
{
    rb_reg_check(re);
    return RBOOL(RREGEXP_PTR(re)->options & ONIG_OPTION_IGNORECASE);
}
encoding → 編碼 按一下以切換來源

傳回表示 obj 編碼的 Encoding 物件。

VALUE
rb_obj_encoding(VALUE obj)
{
    int idx = rb_enc_get_index(obj);
    if (idx < 0) {
        rb_raise(rb_eTypeError, "unknown encoding");
    }
    return rb_enc_from_encoding_index(idx & ENC_INDEX_MASK);
}
eql?
別名為:==
fixed_encoding? → true 或 false 按一下以切換來源

如果 self 適用於具有任何 ASCII 相容編碼的字串,則傳回 false;否則傳回 true

r = /a/                                          # => /a/
r.fixed_encoding?                               # => false
r.match?("\u{6666} a")                          # => true
r.match?("\xa1\xa2 a".force_encoding("euc-jp")) # => true
r.match?("abc".force_encoding("euc-jp"))        # => true

r = /a/u                                        # => /a/
r.fixed_encoding?                               # => true
r.match?("\u{6666} a")                          # => true
r.match?("\xa1\xa2".force_encoding("euc-jp"))   # Raises exception.
r.match?("abc".force_encoding("euc-jp"))        # => true

r = /\u{6666}/                                  # => /\u{6666}/
r.fixed_encoding?                               # => true
r.encoding                                      # => #<Encoding:UTF-8>
r.match?("\u{6666} a")                          # => true
r.match?("\xa1\xa2".force_encoding("euc-jp"))   # Raises exception.
r.match?("abc".force_encoding("euc-jp"))        # => false
static VALUE
rb_reg_fixed_encoding_p(VALUE re)
{
    return RBOOL(FL_TEST(re, KCODE_FIXED));
}
hash → 整數 按一下以切換來源

傳回 self 的整數雜湊值。

相關:Object#hash

VALUE
rb_reg_hash(VALUE re)
{
    st_index_t hashval = reg_hash(re);
    return ST2FIX(hashval);
}
inspect → 字串 按一下以切換來源

傳回 self 的格式良好的字串表示

/ab+c/ix.inspect # => "/ab+c/ix"

相關:Regexp#to_s

static VALUE
rb_reg_inspect(VALUE re)
{
    if (!RREGEXP_PTR(re) || !RREGEXP_SRC(re) || !RREGEXP_SRC_PTR(re)) {
        return rb_any_to_s(re);
    }
    return rb_reg_desc(re);
}
match(字串, offset = 0) → matchdata 或 nil 按一下以切換來源
match(字串, offset = 0) {|matchdata| ... } → 物件

如果未提供區塊,則傳回描述匹配項(如果有的話)的 MatchData 物件,或如果沒有,則傳回 nil;搜尋從 string 中指定的字元 offset 開始

/abra/.match('abracadabra')      # => #<MatchData "abra">
/abra/.match('abracadabra', 4)   # => #<MatchData "abra">
/abra/.match('abracadabra', 8)   # => nil
/abra/.match('abracadabra', 800) # => nil

string = "\u{5d0 5d1 5e8 5d0}cadabra"
/abra/.match(string, 7)          #=> #<MatchData "abra">
/abra/.match(string, 8)          #=> nil
/abra/.match(string.b, 8)        #=> #<MatchData "abra">

如果提供了區塊,則僅在找到匹配項時呼叫區塊;傳回區塊的值

/abra/.match('abracadabra') {|matchdata| p matchdata }
# => #<MatchData "abra">
/abra/.match('abracadabra', 4) {|matchdata| p matchdata }
# => #<MatchData "abra">
/abra/.match('abracadabra', 8) {|matchdata| p matchdata }
# => nil
/abra/.match('abracadabra', 8) {|marchdata| fail 'Cannot happen' }
# => nil

輸出(來自上方前兩個區塊)

#<MatchData "abra">
#<MatchData "abra">

 /(.)(.)(.)/.match("abc")[2] # => "b"
 /(.)(.)/.match("abc", 1)[2] # => "c"
static VALUE
rb_reg_match_m(int argc, VALUE *argv, VALUE re)
{
    VALUE result = Qnil, str, initpos;
    long pos;

    if (rb_scan_args(argc, argv, "11", &str, &initpos) == 2) {
        pos = NUM2LONG(initpos);
    }
    else {
        pos = 0;
    }

    pos = reg_match_pos(re, &str, pos, &result);
    if (pos < 0) {
        rb_backref_set(Qnil);
        return Qnil;
    }
    rb_match_busy(result);
    if (!NIL_P(result) && rb_block_given_p()) {
        return rb_yield(result);
    }
    return result;
}
match?(字串) → true 或 false 按一下以切換來源
match?(字串, offset = 0) → true 或 false

傳回 truefalse 以表示 regexp 是否匹配,而不更新 $~ 和其他相關變數。如果存在第二個參數,則它指定字串中開始搜尋的位置。

/R.../.match?("Ruby")    # => true
/R.../.match?("Ruby", 1) # => false
/P.../.match?("Ruby")    # => false
$&                       # => nil
static VALUE
rb_reg_match_m_p(int argc, VALUE *argv, VALUE re)
{
    long pos = rb_check_arity(argc, 1, 2) > 1 ? NUM2LONG(argv[1]) : 0;
    return rb_reg_match_p(re, argv[0], pos);
}
named_captures → 雜湊 按一下以切換來源

傳回表示 self 的命名擷取的雜湊(請參閱 命名擷取

  • 每個鍵都是命名擷取的名稱。

  • 每個值都是該命名擷取的整數索引陣列。

範例

/(?<foo>.)(?<bar>.)/.named_captures # => {"foo"=>[1], "bar"=>[2]}
/(?<foo>.)(?<foo>.)/.named_captures # => {"foo"=>[1, 2]}
/(.)(.)/.named_captures             # => {}
static VALUE
rb_reg_named_captures(VALUE re)
{
    regex_t *reg = (rb_reg_check(re), RREGEXP_PTR(re));
    VALUE hash = rb_hash_new_with_size(onig_number_of_names(reg));
    onig_foreach_name(reg, reg_named_captures_iter, (void*)hash);
    return hash;
}
names → 名稱陣列 按一下以切換來源

傳回一個擷取名稱的陣列(請參閱 命名擷取

/(?<foo>.)(?<bar>.)(?<baz>.)/.names # => ["foo", "bar", "baz"]
/(?<foo>.)(?<foo>.)/.names          # => ["foo"]
/(.)(.)/.names                      # => []
static VALUE
rb_reg_names(VALUE re)
{
    VALUE ary;
    rb_reg_check(re);
    ary = rb_ary_new_capa(onig_number_of_names(RREGEXP_PTR(re)));
    onig_foreach_name(RREGEXP_PTR(re), reg_names_iter, (void*)ary);
    return ary;
}
options → integer 按一下以切換來源

傳回一個整數,其位元顯示在 self 中設定的選項。

選項位元為

Regexp::IGNORECASE # => 1
Regexp::EXTENDED   # => 2
Regexp::MULTILINE  # => 4

範例

/foo/.options    # => 0
/foo/i.options   # => 1
/foo/x.options   # => 2
/foo/m.options   # => 4
/foo/mix.options # => 7

請注意,傳回的整數中可能會設定其他位元;這些位元會在 self 內部維護,如果傳遞給 Regexp.new 則會略過,而且呼叫者可能會略過

傳回與建立此正規表示式時使用的選項對應的位元組(請參閱 Regexp::new 以取得詳細資料)。請注意,傳回的選項中可能會設定其他位元:這些位元由正規表示式程式碼在內部使用。如果選項傳遞給 Regexp::new,這些額外的位元會略過

r = /\xa1\xa2/e                 # => /\xa1\xa2/
r.source                        # => "\\xa1\\xa2"
r.options                       # => 16
Regexp.new(r.source, r.options) # => /\xa1\xa2/
static VALUE
rb_reg_options_m(VALUE re)
{
    int options = rb_reg_options(re);
    return INT2NUM(options);
}
source → string 按一下以切換來源

傳回 self 的原始字串

/ab+c/ix.source # => "ab+c"

Regexp 逸出序列會保留

/\x20\+/.source  # => "\\x20\\+"

不會保留詞法分析器逸出字元

/\//.source  # => "/"
static VALUE
rb_reg_source(VALUE re)
{
    VALUE str;

    rb_reg_check(re);
    str = rb_str_dup(RREGEXP_SRC(re));
    return str;
}
timeout → 浮點數或 nil 按一下以切換來源

它傳回 Regexp 匹配的逾時區間(單位為秒)。nil 表示沒有預設逾時設定。

此設定是針對每個物件。如果設定每個物件的設定,Regexp.timeout= 設定的全球設定會略過。

re = Regexp.new("^a*b?a*$", timeout: 1)
re.timeout               #=> 1.0
re =~ "a" * 100000 + "x" #=> regexp match timeout (RuntimeError)
static VALUE
rb_reg_timeout_get(VALUE re)
{
    rb_reg_check(re);
    double d = hrtime2double(RREGEXP_PTR(re)->timelimit);
    if (d == 0.0) return Qnil;
    return DBL2NUM(d);
}
to_json(*args) 按一下以切換來源

傳回一個表示 selfJSON 字串

require 'json/add/regexp'
puts /foo/.to_json

輸出

{"json_class":"Regexp","o":0,"s":"foo"}
# File ext/json/lib/json/add/regexp.rb, line 45
def to_json(*args)
  as_json.to_json(*args)
end
to_s → string 按一下以切換來源

傳回一個顯示 self 的選項和字串的字串

r0 = /ab+c/ix
s0 = r0.to_s # => "(?ix-m:ab+c)"

傳回的字串可用作 Regexp.new 的引數,或作為 正規表示式內插 的內插文字

r1 = Regexp.new(s0) # => /(?ix-m:ab+c)/
r2 = /#{s0}/        # => /(?ix-m:ab+c)/

請注意,r1r2 不等於 r0,因為它們的原始字串不同

r0 == r1  # => false
r0.source # => "ab+c"
r1.source # => "(?ix-m:ab+c)"

相關:Regexp#inspect

static VALUE
rb_reg_to_s(VALUE re)
{
    return rb_reg_str_with_term(re, '/');
}
~ rxp → integer or nil 按一下以切換來源

等同於 rxp =~ $_

$_ = "input data"
~ /at/ # => 7
VALUE
rb_reg_match2(VALUE re)
{
    long start;
    VALUE line = rb_lastline_get();

    if (!RB_TYPE_P(line, T_STRING)) {
        rb_backref_set(Qnil);
        return Qnil;
    }

    start = rb_reg_search(re, line, 0, 0);
    if (start < 0) {
        return Qnil;
    }
    start = rb_str_sublen(line, start);
    return LONG2FIX(start);
}