Skip to content

Ruby 正则表达式

正则表达式 (regex) 是一系列定义搜索模式的字符。在 Ruby 中,正则表达式是 Regexp 类的对象,广泛用于字符串的搜索、匹配和操作。

你可以使用字面量形式 /pattern/ 或 %r{pattern}(如果模式包含许多斜杠时很有用)创建 Regexp 对象,或者使用 Regexp.new("pattern")。

/your_pattern_here/
/your_pattern_here/im # 'i' for case-insensitive, 'm' for multiline
%r{another_pattern} # General delimited regular expression, good for patterns with slashes
Regexp.new("dynamic_pattern")

=~ 运算符检查字符串是否匹配 regex,如果匹配则返回匹配的起始索引,如果未找到匹配项则返回 nil。String#match 方法通常更有用,因为它返回一个 MatchData 对象,其中包含有关匹配项的详细信息(包括捕获),如果未找到匹配项则返回 nil。

#!/usr/bin/env ruby
text1 = "Ruby is a fun language."
text2 = "Python is also cool."
# Using =~ operator
if text1 =~ /Ruby/
puts "text1 contains 'Ruby' (using =~)"
end
# Using String#match
match_data = text1.match(/fun (\w+)/) # (\w+) 是一个捕获组
if match_data
puts "text1 contains 'fun language' (using match)"
puts "The matched word after 'fun': #{match_data[1]}" # 访问第一个捕获组
puts "Full match: #{match_data[0]}" # 完整匹配: #{match_data[0]}
end
if text2.match(/Ruby/)
puts "text2 contains 'Ruby'"
else
puts "text2 does not contain 'Ruby'"
end

这将产生以下结果:

text1 contains 'Ruby' (using =~)
text1 contains 'fun language' (using match)
The matched word after 'fun': language
Full match: fun language
text2 does not contain 'Ruby'

修饰符改变 regex 模式的解释方式。它们放在字面量 regex 的结束分隔符之后(例如,/pattern/i)。

修饰符描述
i不区分大小写:匹配时忽略大小写。
m多行模式:点 (.) 元字符匹配换行符。^ 和 $ 匹配行的开始/结束。
x扩展模式:忽略空白字符(除非被转义或在字符类中),并允许在 regex 中使用注释 (# ...)。对复杂模式很有用。
o一次:只在 regex 字面量首次评估时执行一次 #{...} 插值。在现代 Ruby 中较不常见,因为性能已有所改进。

Ruby 的默认字符串编码是 UTF-8,regexes 可以正确处理 Unicode 字符。除非处理特定编码的旧数据,否则现代 Ruby 通常不需要用于特定编码的旧修饰符(如 u, e, s, n)。

大多数字符匹配它们本身。特殊的元字符,如 . * + ? ^ $ ( ) [ ] { } | \ 具有特定含义,如果你想按字面意思匹配它们,必须用反斜杠 (\) 转义。

下表列出了常见的 regex 语法元素:

模式描述
^匹配行的开始(如果不在多行模式下,则匹配字符串的开始)。
$匹配行的结束(如果不在多行模式下,则在字符串末尾换行符之前)。
.匹配除换行符以外的任何单个字符(除非在多行模式下使用 /m)。
[…]字符类:匹配括号内的任何单个字符。例如,[aeiou] 匹配任何元音字母。
[^…]否定字符类:匹配不在括号内的任何单个字符。
re*匹配前面表达式 re 出现 0 次或多次(贪婪)。
re+匹配 re 出现 1 次或多次(贪婪)。
re?匹配 re 出现 0 次或 1 次(贪婪)。
re*?匹配 0 次或多次出现(非贪婪/惰性)。
re+?匹配 1 次或多次出现(非贪婪/惰性)。
re??匹配 0 次或 1 次出现(非贪婪/惰性)。
re{n}精确匹配 re 出现 n 次。
re{n,}匹配 re 出现 n 次或更多次。
re{n,m}匹配 re 出现至少 n 次且最多 m 次。
a|b交替:匹配 a 或 b。
(re)捕获组:分组 re 并捕获匹配的文本。通过 MatchData 对象或 $1、$2 等访问。
(?re)命名捕获组:捕获 re 并为其分配一个名称。通过 match_data[:name] 访问。
(?:re)非捕获组:分组 re 但不捕获匹配项。
\w匹配一个单词字符(字母数字加下划线:[a-zA-Z0-9_])
\W匹配一个非单词字符。
\s匹配一个空白字符(空格、制表符、换行符等)。
\S匹配一个非空白字符。
\d匹配一个数字 ([0-9])。
\D匹配一个非数字。
\A匹配字符串的开始。
\Z匹配字符串的结束,或字符串末尾的换行符之前。
\z匹配字符串的绝对结束。
\b匹配一个单词边界(单词字符和非单词字符之间的位置)。
\B匹配一个非单词边界。
\1, \2, …反向引用:匹配第 N 个捕获组捕获的文本。
\k反向引用:匹配名为 ‘name’ 的组捕获的文本。

在匹配整个字符串时,使用 \A(字符串开始)和 \z(字符串结束)通常比使用 ^ 和 $ 更安全。如果使用了 /m 修饰符,^ 和 $ 可以匹配多行字符串中的行开始/结束,这可能不是输入验证所需的行为。

命名捕获使复杂 regex 更具可读性和可维护性。

#!/usr/bin/env ruby
log_line = "INFO: Request processed in 0.025s by UserController#index"
pattern = /^(?<level>\w+): (?<message>.*) in (?<duration>\d+\.\d+)s by (?<controller>\w+)#(?<action>\w+)$/
match = log_line.match(pattern)
if match
puts "Log Level: #{match[:level]}" # Access by name
puts "Message: #{match['message']}" # Access by name as string
puts "Duration: #{match[:duration]}"
puts "Controller: #{match[:controller]}"
puts "Action: #{match[:action]}"
else
puts "Log line did not match the pattern."
end

这将产生:

Log Level: INFO
Message: Request processed
Duration: 0.025
Controller: UserController
Action: index

String 类提供了 sub(替换第一个匹配项)和 gsub(替换所有匹配项)方法,用于使用正则表达式进行搜索和替换操作。它们的带感叹号版本(sub! 和 gsub!)会就地修改字符串。

#!/usr/bin/env ruby
phone = "Call (123) 456-7890 or 987-654-3210 # Support lines"
# Remove comments
cleaned_phone = phone.sub(/\s*#.*$/, "")
puts "Cleaned Phone: #{cleaned_phone}" #=> Call (123) 456-7890 or 987-654-3210
# Standardize phone format (example, very basic)
# This gsub uses a block for replacement logic
standardized_phone = cleaned_phone.gsub(/\(?(\d{3})\)?[\s-]?(\d{3})[\s-]?(\d{4})/) do |match_text|
# $1, $2, $3 引用当前匹配中的捕获组
"+#{$1}-#{$2}-#{$3}"
end
puts "Standardized Phone: #{standardized_phone}" #=> Call +123-456-7890 or +987-654-3210
text = "ruby is fun, Ruby is powerful."
# Case-insensitive replacement of "ruby"
new_text = text.gsub(/ruby/i, "Crystal")
puts "New text: #{new_text}" #=> Crystal is fun, Crystal is powerful.

sub 和 gsub 中的替换参数也可以是哈希,其中键是匹配的字符串,值是它们的替换项;或者可以使用反向引用,如 \1、\2 或 \k<name>。

其他涉及 regex 的有用 String 方法包括 scan(返回所有匹配项的数组)、split(按 regex 分隔符分割字符串)和 index(查找第一个匹配项的索引)。