类 Regexp
一个 正则表达式(也称为regexp)是一个匹配模式(也简称为模式)。
regexp 的常用表示法使用封闭的斜杠字符
/foo/
regexp 可以应用于目标字符串;字符串中匹配模式的部分(如果有)称为匹配,可以被认为匹配
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 用途¶ ↑
regexp 可以用于
-
根据给定模式提取子字符串
re = /foo/ # => /foo/ re.match('food') # => #<MatchData "foo"> re.match('good') # => nil
-
确定字符串是否匹配给定模式
re.match?('food') # => true re.match?('good') # => false
请参阅 Method match? 部分。
-
作为其他类和模块中某些方法调用的参数;大多数此类方法接受一个参数,该参数可以是字符串或(更强大的)正则表达式。
请参阅 Regexp Methods。
Regexp 对象¶ ↑
一个正则表达式对象具有
创建正则表达式¶ ↑
可以使用以下方法创建正则表达式
-
使用斜杠字符的正则表达式字面量(请参阅 Regexp Literals)
# This is a very common usage. /foo/ # => /foo/
-
%r
正则表达式字面量(请参阅 %r: Regexp Literals)# Same delimiter character at beginning and end; # useful for avoiding escaping characters %r/name\/value pair/ # => /name\/value pair/ %r:name/value pair: # => /name\/value pair/ %r|name/value pair| # => /name\/value pair/ # Certain "paired" characters can be delimiters. %r[foo] # => /foo/ %r{foo} # => /foo/ %r(foo) # => /foo/ %r<foo> # => /foo/
-
方法
Regexp.new
。
方法 match
¶ ↑
每个方法 Regexp#match
、String#match
和 Symbol#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
全局变量¶ ↑
某些与正则表达式相关的函数会将值分配给全局变量
-
#match
:请参阅 Method match。 -
#=~
:请参阅 Operator =~。
受影响的全局变量是
-
$~
: 返回一个MatchData
对象,或nil
。 -
$&
: 返回字符串中匹配的部分,或nil
。 -
$`
: 返回匹配左侧的字符串部分,或nil
。 -
$'
: 返回匹配右侧的字符串部分,或nil
。 -
$+
: 返回最后一个匹配的组,或nil
。 -
$1
,$2
等:返回第一个、第二个等匹配的组,或nil
。注意$0
不同;它返回当前执行程序的名称。
示例
# 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 "\\.">
Method
Regexp.escape
返回一个转义的字符串
Regexp.escape('.?-+*^\|$()[]{}') # => "\\.\\?\\-\\+\\*\\^\\\\\\|\\$\\(\\)\\[\\]\\{\\}"
源字面量¶ ↑
源字面量在很大程度上表现得像双引号字符串;请参阅 字符串字面量。
特别是,源字面量可以包含插值表达式
s = 'foo' # => "foo" /#{s}/ # => /foo/ /#{s.capitalize}/ # => /Foo/ /#{2 + 2}/ # => /4/
普通字符串字面量和源字面量之间存在差异;请参阅 简写字符类。
-
普通字符串字面量中的
\s
等效于空格字符;在源字面量中,它是匹配空格字符的简写。 -
在普通字符串字面量中,这些是(不必要地)转义的字符;在源字面量中,它们是各种匹配字符的简写
\w \W \d \D \h \H \S \R
字符类¶ ↑
字符类 由方括号包围;它指定在目标字符串的给定位置匹配某些字符。
# 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]/
简写字符类¶ ↑
以下每个元字符都是字符类的简写
-
/./
:匹配除换行符以外的任何字符/./.match('foo') # => #<MatchData "f"> /./.match("\n") # => nil
-
/./m
:匹配任何字符,包括换行符;参见 多行模式/./m.match("\n") # => #<MatchData "\n">
-
/\w/
:匹配一个单词字符:等效于[a-zA-Z0-9_]
/\w/.match(' foo') # => #<MatchData "f"> /\w/.match(' _') # => #<MatchData "_"> /\w/.match(' ') # => nil
-
/\W/
:匹配一个非单词字符:等效于[^a-zA-Z0-9_]
/\W/.match(' ') # => #<MatchData " "> /\W/.match('_') # => nil
-
/\d/
:匹配一个数字字符:等效于[0-9]
/\d/.match('THX1138') # => #<MatchData "1"> /\d/.match('foo') # => nil
-
/\D/
:匹配一个非数字字符:等效于[^0-9]
/\D/.match('123Jump!') # => #<MatchData "J"> /\D/.match('123') # => nil
-
/\h/
:匹配一个十六进制数字字符:等效于[0-9a-fA-F]
/\h/.match('xyz fedcba9876543210') # => #<MatchData "f"> /\h/.match('xyz') # => nil
-
/\H/
:匹配一个非十六进制数字字符:等效于[^0-9a-fA-F]
/\H/.match('fedcba9876543210xyz') # => #<MatchData "x"> /\H/.match('fedcba9876543210') # => nil
-
/\s/
:匹配一个空白字符:等效于/[ \t\r\n\f\v]/
/\s/.match('foo bar') # => #<MatchData " "> /\s/.match('foo') # => nil
-
/\S/
:匹配一个非空白字符:等效于/[^ \t\r\n\f\v]/
/\S/.match(" \t\r\n\f\v foo") # => #<MatchData "f"> /\S/.match(" \t\r\n\f\v") # => nil
-
/\R/
:匹配一个换行符,与平台无关/\R/.match("\r") # => #<MatchData "\r"> # Carriage return (CR) /\R/.match("\n") # => #<MatchData "\n"> # Newline (LF) /\R/.match("\f") # => #<MatchData "\f"> # Formfeed (FF) /\R/.match("\v") # => #<MatchData "\v"> # Vertical tab (VT) /\R/.match("\r\n") # => #<MatchData "\r\n"> # CRLF /\R/.match("\u0085") # => #<MatchData "\u0085"> # Next line (NEL) /\R/.match("\u2028") # => #<MatchData "\u2028"> # Line separator (LSEP) /\R/.match("\u2029") # => #<MatchData "\u2029"> # Paragraph separator (PSEP)
锚点¶ ↑
锚点是一个元序列,它匹配目标字符串中字符之间的零宽度位置。
对于没有锚点的子表达式,匹配可以从目标字符串中的任何位置开始
/real/.match('surrealist') # => #<MatchData "real">
对于带有锚点的子表达式,匹配必须从匹配的锚点开始。
边界锚点¶ ↑
这些锚点中的每一个都匹配一个边界
-
^
:匹配一行的开头/^bar/.match("foo\nbar") # => #<MatchData "bar"> /^ar/.match("foo\nbar") # => nil
-
$
:匹配一行的结尾/bar$/.match("foo\nbar") # => #<MatchData "bar"> /ba$/.match("foo\nbar") # => nil
-
\A
:匹配字符串的开头/\Afoo/.match('foo bar') # => #<MatchData "foo"> /\Afoo/.match(' foo bar') # => nil
-
\Z
:匹配字符串的结尾;如果字符串以单个换行符结尾,它匹配结束换行符之前的部分/foo\Z/.match('bar foo') # => #<MatchData "foo"> /foo\Z/.match('foo bar') # => nil /foo\Z/.match("bar foo\n") # => #<MatchData "foo"> /foo\Z/.match("bar foo\n\n") # => nil
-
\z
:匹配字符串的结尾/foo\z/.match('bar foo') # => #<MatchData "foo"> /foo\z/.match('foo bar') # => nil /foo\z/.match("bar foo\n") # => nil
-
\b
: 在括号外匹配单词边界;在括号内匹配退格键 ("0x08"
)/foo\b/.match('foo bar') # => #<MatchData "foo"> /foo\b/.match('foobar') # => nil
-
\B
: 匹配非单词边界/foo\B/.match('foobar') # => #<MatchData "foo"> /foo\B/.match('foo bar') # => nil
-
\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
环视锚点¶ ↑
先行断言
-
(?=pat)
: 正向先行断言:确保后面的字符匹配 pat,但不包含这些字符在匹配的子字符串中。 -
(?!pat)
: 负向先行断言:确保后面的字符不匹配 pat,但不包含这些字符在匹配的子字符串中。
后行断言
-
(?<=pat)
: 正向后行断言:确保前面的字符匹配 pat,但不包含这些字符在匹配的子字符串中。 -
(?<!pat)
: 负向后行断言:确保前面的字符不匹配 pat,但不包含这些字符在匹配的子字符串中。
下面的模式使用正向先行和正向后行来匹配出现在 … 标签中的文本,但不包括标签在匹配中
/(?<=<b>)\w+(?=<\/b>)/.match("Fortune favors the <b>bold</b>.") # => #<MatchData "bold">
匹配重置锚点¶ ↑
-
\K
: 匹配重置:正则表达式中\K
之前的匹配内容将从结果中排除。例如,以下两个正则表达式几乎等效/ab\Kc/.match('abc') # => #<MatchData "c"> /(?<=ab)c/.match('abc') # => #<MatchData "c">
它们匹配相同的字符串,并且
$&
等于'c'
,而匹配位置不同。以下两个正则表达式也是如此
/(a)\K(b)\Kc/ /(?<=(?<=(a))(b))c/
选择¶ ↑
竖线元字符 (|
) 可以用在括号内来表示选择:两个或多个子表达式,其中任何一个都可以匹配目标字符串。
两个备选方案
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">
添加一个量词指定需要或允许的匹配次数
-
*
- 匹配零次或多次/\w*/.match('') # => #<MatchData ""> /\w*/.match('x') # => #<MatchData "x"> /\w*/.match('xyz') # => #<MatchData "yz">
-
+
- 匹配一次或多次/\w+/.match('') # => nil /\w+/.match('x') # => #<MatchData "x"> /\w+/.match('xyz') # => #<MatchData "xyz">
-
?
- 匹配零次或一次/\w?/.match('') # => #<MatchData ""> /\w?/.match('x') # => #<MatchData "x"> /\w?/.match('xyz') # => #<MatchData "x">
-
{
n}
- 匹配正好n次/\w{2}/.match('') # => nil /\w{2}/.match('x') # => nil /\w{2}/.match('xyz') # => #<MatchData "xy">
-
{
min,}
- 匹配min次或更多次/\w{2,}/.match('') # => nil /\w{2,}/.match('x') # => nil /\w{2,}/.match('xy') # => #<MatchData "xy"> /\w{2,}/.match('xyz') # => #<MatchData "xyz">
-
{,
max}
- 匹配max次或更少次/\w{,2}/.match('') # => #<MatchData ""> /\w{,2}/.match('x') # => #<MatchData "x"> /\w{,2}/.match('xyz') # => #<MatchData "xy">
-
{
min,
max}
- 匹配至少min次,最多max次/\w{1,2}/.match('') # => nil /\w{1,2}/.match('x') # => #<MatchData "x"> /\w{1,2}/.match('xyz') # => #<MatchData "xy">
贪婪、懒惰或占有匹配¶ ↑
量词匹配可以是贪婪的、懒惰的或占有的
-
在贪婪匹配中,只要允许整体匹配成功,就会匹配尽可能多的出现次数。贪婪量词:
*
、+
、?
、{min, max}
及其变体。 -
在懒惰匹配中,匹配最少的出现次数。懒惰量词:
*?
、+?
、??
、{min, max}?
及其变体。 -
在占有匹配中,一旦找到匹配项,就不会回溯;该匹配将被保留,即使它会危及整体匹配。占有量词:
*+
、++
、?+
。请注意,{min, max}
及其变体不支持占有匹配。
更多
组和捕获¶ ↑
一个简单的正则表达式最多只有一个匹配
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
添加一对或多对括号,(subexpression)
,定义组,这可能导致多个匹配的子字符串,称为捕获
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' |
一个正则表达式可以包含任意数量的组。
-
对于大量的组。
-
普通的
\n
表示法仅适用于范围 (1..9) 内的 n。 -
MatchData[n]
表示法适用于任何非负 n。
-
-
\0
是一个特殊的反向引用,它引用整个匹配的字符串;它不能在正则表达式本身中使用,但可以在其外部使用(例如,在替换方法调用中)。'The cat sat in the hat'.gsub(/[csh]at/, '\0s') # => "The cats sats in the hats"
命名捕获¶ ↑
如上所述,捕获可以通过其编号来引用。捕获也可以有一个名称,以?<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
返回捕获名称的数组。
原子分组¶ ↑
一个组可以使用(?>
subexpression)
设置为原子。
这会导致子表达式独立于表达式的其余部分进行匹配,因此匹配的子字符串对于匹配的其余部分将被固定,除非整个子表达式必须被放弃并随后重新访问。
这样,subexpression 被视为不可分割的整体。原子分组通常用于优化模式以防止不必要的回溯。
示例(没有原子分组)
/".*"/.match('"Quote"') # => #<MatchData "\"Quote\"">
分析
-
模式中的前导子表达式
"
匹配目标字符串中的第一个字符"
。 -
下一个子表达式
.*
匹配下一个子字符串Quote“
(包括尾部的双引号)。 -
现在目标字符串中没有剩余的内容来匹配模式中的尾随子表达式
"
;这将导致整体匹配失败。 -
匹配的子字符串被回溯一个位置:
Quote
。 -
最终的子表达式
"
现在匹配最终的子字符串"
,并且整体匹配成功。
如果子表达式 .*
被原子化分组,则回溯被禁用,并且整体匹配失败。
/"(?>.*)"/.match('"Quote"') # => nil
原子分组会影响性能;请参阅 原子分组。
子表达式调用¶ ↑
如上所示,反向引用编号 (\n
) 或名称 (\k<name>
) 可以访问捕获的子字符串;相应的正则表达式子表达式也可以通过编号 (\gn
) 或名称 (\g<name>
) 访问。
/\A(?<paren>\(\g<paren>*\))*\z/.match('(())') # ^1 # ^2 # ^3 # ^4 # ^5 # ^6 # ^7 # ^8 # ^9 # ^10
模式
-
在字符串的开头匹配,即第一个字符之前。
-
进入名为
paren
的命名组。 -
匹配字符串中的第一个字符,
'('
。 -
再次调用
paren
组,即递归回到第二步。 -
重新进入
paren
组。 -
匹配字符串中的第二个字符,
'('
。 -
尝试第三次调用
paren
,但失败,因为这样做会阻止整体成功匹配。 -
匹配字符串中的第三个字符,
')'
;标记第二次递归调用的结束。 -
匹配字符串中的第四个字符,
')'
。 -
匹配字符串的结尾。
请参阅 子表达式调用。
条件¶ ↑
条件结构采用 (?(cond)yes|no)
的形式,其中
-
cond 可以是捕获编号或名称。
-
如果捕获了 cond,则要应用的匹配是 yes;否则要应用的匹配是 no。
-
如果不需要,可以省略
|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 方括号表达式。
-
/\p{Alnum}/
:字母数字字符。 -
/\p{Alpha}/
:字母字符。 -
/\p{Blank}/
:空格或制表符。 -
/\p{Cntrl}/
:控制字符。 -
/\p{Digit}/
:数字字符,以及类似的字符。 -
/\p{Lower}/
:小写字母字符。 -
/\p{Print}/
:与\p{Graph}
相似,但包含空格字符。 -
/\p{Punct}/
:标点符号字符。 -
/\p{Space}/
:空白字符([:blank:]
、换行符、回车符等)。 -
/\p{Upper}/
:大写字母。 -
/\p{XDigit}/
:十六进制数中允许的数字(即 0-9a-fA-F)。
以下也是常用的属性。
-
/\p{Emoji}/
:Unicode 表情符号。 -
/\p{Graph}/
:非空白字符(不包括空格、控制字符等)。 -
/\p{Word}/
:以下 Unicode 字符类别中的成员之一,或具有以下 Unicode 属性之一。-
Unicode 类别
-
Mark
(M
)。 -
Decimal Number
(Nd
) -
Connector Punctuation
(Pc
)。
-
-
Unicode 属性
-
Alpha
-
Join_Control
-
-
-
/\p{ASCII}/
:ASCII 字符集中的字符。 -
/\p{Any}/
:任何 Unicode 字符(包括未分配的字符)。 -
/\p{Assigned}/
:已分配的字符。
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 字符类别缩写和名称。每个类别中的字符枚举在链接中。
字母
-
L
,Letter
:LC
,Lm
, 或Lo
。 -
LC
,Cased_Letter
:Ll
,Lt
, 或Lu
。
标记
数字
标点符号
-
P
,标点符号
:Pc
,Pd
,Pe
,Pf
,Pi
,Po
,或Ps
。 -
S
,符号
:Sc
,Sk
,Sm
,或So
。 -
Z
,分隔符
:Zl
,Zp
,或Zs
。 -
C
,其他
:Cc
,Cf
,Cn
,Co
,或Cs
。
Unicode 脚本和块¶ ↑
Unicode 属性中包括
-
Unicode 脚本;参见 支持的脚本.
POSIX 方括号表达式¶ ↑
POSIX 方括号表达式 也类似于字符类。这些表达式提供了一种可移植的替代方案,并具有包含非 ASCII 字符的额外优势。
-
/\d/
仅匹配 ASCII 十进制数字0
到9
。 -
/[[:digit:]]/
匹配 Unicode十进制数字
(Nd
) 类中的任何字符;参见下文。
POSIX 方括号表达式
-
/[[:digit:]]/
:匹配 Unicode 数字/[[:digit:]]/.match('9') # => #<MatchData "9"> /[[:digit:]]/.match("\u1fbf9") # => #<MatchData "9">
-
/[[:xdigit:]]/
:匹配十六进制数中允许的数字;等效于[0-9a-fA-F]
。 -
/[[:upper:]]/
:匹配 Unicode 大写字母/[[:upper:]]/.match('A') # => #<MatchData "A"> /[[:upper:]]/.match("\u00c6") # => #<MatchData "Æ">
-
/[[:lower:]]/
:匹配 Unicode 小写字母/[[:lower:]]/.match('a') # => #<MatchData "a"> /[[:lower:]]/.match("\u01fd") # => #<MatchData "ǽ">
-
/[[:alpha:]]/
:匹配/[[:upper:]]/
或/[[:lower:]]/
。 -
/[[:alnum:]]/
:匹配/[[:alpha:]]/
或/[[:digit:]]/
。 -
/[[:space:]]/
: 匹配 Unicode 空格字符/[[:space:]]/.match(' ') # => #<MatchData " "> /[[:space:]]/.match("\u2005") # => #<MatchData " ">
-
/[[:blank:]]/
: 匹配/[[:space:]]/
或制表符/[[:blank:]]/.match(' ') # => #<MatchData " "> /[[:blank:]]/.match("\u2005") # => #<MatchData " "> /[[:blank:]]/.match("\t") # => #<MatchData "\t">
-
/[[:cntrl:]]/
: 匹配 Unicode 控制字符/[[:cntrl:]]/.match("\u0000") # => #<MatchData "\u0000"> /[[:cntrl:]]/.match("\u009f") # => #<MatchData "\u009F">
-
/[[:graph:]]/
: 匹配除/[[:space:]]/
或/[[:cntrl:]]/
之外的任何字符。 -
/[[:print:]]/
: 匹配/[[:graph:]]/
或空格字符。 -
/[[:punct:]]/
: 匹配任何(Unicode 标点符号字符}[www.compart.com/en/unicode/category/Po]
Ruby 还支持这些(非 POSIX)方括号表达式
-
/[[:ascii:]]/
: 匹配 ASCII 字符集中的字符。 -
/[[:word:]]/
: 匹配以下 Unicode 字符类别之一的字符,或具有以下 Unicode 属性之一的字符-
Unicode 类别
-
Mark
(M
)。 -
Decimal Number
(Nd
) -
Connector Punctuation
(Pc
)。
-
-
Unicode 属性
-
Alpha
-
Join_Control
-
-
注释¶ ↑
可以使用 (?#
注释)
结构在正则表达式模式中包含注释,其中 注释 是要忽略的子字符串。正则表达式引擎会忽略任意文本
/foo(?#Ignore me)bar/.match('foobar') # => #<MatchData "foobar">
注释不能包含未转义的终止符。
另请参见 扩展模式。
模式¶ ↑
这些修饰符中的每一个都为正则表达式设置一个模式
可以应用这些修饰符中的任何一个、所有或都不应用。
修饰符 i
、m
和 x
可以应用于子表达式
-
(?modifier)
为后续子表达式“打开”模式 -
(?-modifier)
为后续子表达式“关闭”模式 -
(?modifier:subexp)
为组内的 subexp “打开”模式 -
(?-modifier:subexp)
为组内的 subexp “关闭”模式
示例
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
返回一个整数,其值显示不区分大小写模式、多行模式和扩展模式的设置。
不区分大小写模式¶ ↑
默认情况下,正则表达式区分大小写
/foo/.match('FOO') # => nil
修饰符 i
启用不区分大小写模式
/foo/i.match('FOO') # => #<MatchData "FOO">
方法 Regexp#casefold?
返回模式是否不区分大小写。
多行模式¶ ↑
Ruby 中的多行模式通常被称为“点号全模式”
-
如果没有
m
修饰符,子表达式.
不会匹配换行符/a.c/.match("a\nc") # => nil
-
使用修饰符后,它就会匹配
/a.c/m.match("a\nc") # => #<MatchData "a\nc">
与其他语言不同,修饰符m
不会影响锚点^
和$
。在Ruby中,这些锚点始终匹配行边界。
扩展模式¶ ↑
修饰符x
启用扩展模式,这意味着
-
模式中的文字空格将被忽略。
-
字符
#
将它所在行的剩余部分标记为注释,在匹配时也会被忽略。
在扩展模式下,空格和注释可以用来形成自文档化的正则表达式。
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
对象将被保存,并用于该文字正则表达式的所有后续评估。如果没有修饰符o
,生成的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
请注意,如果文字正则表达式不包含插值,则o
行为是默认行为。
编码¶ ↑
默认情况下,仅包含 US-ASCII 字符的正则表达式具有 US-ASCII 编码
re = /foo/ re.source.encoding # => #<Encoding:US-ASCII> re.encoding # => #<Encoding:US-ASCII>
包含非 US-ASCII 字符的正则表达式假定使用源编码。这可以通过以下修饰符之一来覆盖。
-
/pat/n
: 如果仅包含 US-ASCII 字符,则为 US-ASCII,否则为 ASCII-8BIT/foo/n.encoding # => #<Encoding:US-ASCII> /foo\xff/n.encoding # => #<Encoding:ASCII-8BIT> /foo\x7f/n.encoding # => #<Encoding:US-ASCII>
-
/pat/u
: UTF-8/foo/u.encoding # => #<Encoding:UTF-8>
-
/pat/e
: EUC-JP/foo/e.encoding # => #<Encoding:EUC-JP>
-
/pat/s
: Windows-31J/foo/s.encoding # => #<Encoding:Windows-31J>
当满足以下条件之一时,正则表达式可以与目标字符串匹配
-
它们具有相同的编码。
-
正则表达式的编码是固定编码,并且字符串仅包含 ASCII 字符。
Method
Regexp#fixed_encoding?
返回正则表达式是否具有固定编码。
如果尝试在不兼容的编码之间进行匹配,则会引发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 有两个超时值
-
一个类默认超时,用于实例超时为
nil
的正则表达式;此默认值最初为nil
,可以通过方法Regexp.timeout=
设置Regexp.timeout # => nil Regexp.timeout = 3.0 Regexp.timeout # => 3.0
-
一个实例超时,默认为
nil
,可以在Regexp.new
中设置re = Regexp.new('foo', timeout: 5.0) re.timeout # => 5.0
当 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) 攻击。
Regexp 匹配可以应用优化来防止 ReDoS 攻击。当应用优化时,匹配时间随着输入大小呈线性增长(而不是多项式或指数增长),ReDoS 攻击将不再可能。
如果模式满足以下条件,则会应用此优化
-
没有反向引用。
-
没有子表达式调用。
-
没有嵌套的前瞻锚点或原子组。
-
没有嵌套的带计数的量词(即没有嵌套的
{n}
、{min,}
、{,max}
或{min,max}
样式的量词)
可以使用方法 Regexp.linear_time?
来确定模式是否满足这些条件
Regexp.linear_time?(/a*/) # => true Regexp.linear_time?('a*') # => true Regexp.linear_time?(/(a*)\1/) # => false
但是,即使方法返回 true
,不受信任的来源也可能不安全,因为优化使用记忆化(这可能会导致大量内存消耗)。
参考¶ ↑
阅读(在线 PDF 书籍)
探索,测试(交互式在线编辑器)
常量
- EXTENDED
参见
Regexp.options
和Regexp.new
- FIXEDENCODING
参见
Regexp.options
和Regexp.new
- 忽略大小写
参见
Regexp.options
和Regexp.new
- 多行
参见
Regexp.options
和Regexp.new
- 不编码
参见
Regexp.options
和Regexp.new
公共类方法
是 Regexp.new
的别名
返回一个新的字符串,该字符串对正则表达式中具有特殊含义的任何字符进行转义
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)); }
参见 as_json
.
# File ext/json/lib/json/add/regexp.rb, line 9 def self.json_create(object) new(object['s'], object['o']) end
如果没有参数,则返回 $!
的值,它是最近一次模式匹配的结果(参见 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(); }
如果与 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))); }
如果给出参数 string
,则返回一个使用给定字符串和选项的新正则表达式
r = Regexp.new('foo') # => /foo/ r.source # => "foo" r.options # => 0
可选参数 options
是以下之一
-
一个
String
选项Regexp.new('foo', 'i') # => /foo/i Regexp.new('foo', 'im') # => /foo/im
-
一个或多个常量的按位或运算,这些常量包括
Regexp::EXTENDED
、Regexp::IGNORECASE
、Regexp::MULTILINE
和Regexp::NOENCODING
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
-
nil
或false
,会被忽略。 -
任何其他真值,在这种情况下,正则表达式将不区分大小写。
如果提供了可选的关键字参数 timeout
,它的浮点值将覆盖类的超时间隔,Regexp.timeout
。如果传递 nil
作为 +timeout,它将使用类的超时间隔,Regexp.timeout
。
如果给定了参数 regexp
,则返回一个新的正则表达式。源代码、选项、超时与 regexp
相同。options
和 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; }
返回一个新的字符串,该字符串对正则表达式中具有特殊含义的任何字符进行转义
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)); }
它返回 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); }
它设置 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; }
如果 object
是一个正则表达式,则返回 object
Regexp.try_convert(/re/) # => /re/
否则,如果 object
响应 :to_regexp
,则调用 object.to_regexp
并返回结果。
如果 object
不响应 :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); }
返回一个新的正则表达式,它是给定模式的并集。
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); }
公共实例方法
如果 object
是另一个正则表达式,其模式、标志和编码与 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
如果 self
在 string
中找到匹配项,则返回 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); }
返回 self
和 string
的第一个匹配项的整数索引(以字符为单位),如果不存在则返回 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); }
方法 Regexp#as_json
和 Regexp.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
如果 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); }
返回表示 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); }
如果 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)); }
返回 self
的整数哈希值。
相关:Object#hash
.
VALUE rb_reg_hash(VALUE re) { st_index_t hashval = reg_hash(re); return ST2FIX(hashval); }
返回 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); }
如果没有给出代码块,则返回描述匹配项的 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; }
返回 true
或 false
以指示正则表达式是否匹配,而不更新 $~ 和其他相关变量。如果存在第二个参数,则它指定字符串中开始搜索的位置。
/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); }
返回表示 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; }
返回捕获名称的数组(参见 命名捕获)。
/(?<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; }
返回一个整数,其位表示在 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); }
返回 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; }
它返回 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); }
返回一个表示 self
的 JSON
字符串。
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
返回一个字符串,显示 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)/
注意,r1
和 r2
不等于 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 =~ $_
$_ = "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); }