Ruby 正则表达式
Ruby 正则表达式
Section titled “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 slashesRegexp.new("dynamic_pattern")使用 =~ 和 String#match 进行匹配:
Section titled “使用 =~ 和 String#match 进行匹配:”=~ 运算符检查字符串是否匹配 regex,如果匹配则返回匹配的起始索引,如果未找到匹配项则返回 nil。String#match 方法通常更有用,因为它返回一个 MatchData 对象,其中包含有关匹配项的详细信息(包括捕获),如果未找到匹配项则返回 nil。
#!/usr/bin/env ruby
text1 = "Ruby is a fun language."text2 = "Python is also cool."
# Using =~ operatorif text1 =~ /Ruby/ puts "text1 contains 'Ruby' (using =~)"end
# Using String#matchmatch_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': languageFull match: fun languagetext2 does not contain 'Ruby'正则表达式修饰符:
Section titled “正则表达式修饰符:”修饰符改变 regex 模式的解释方式。它们放在字面量 regex 的结束分隔符之后(例如,/pattern/i)。
| 修饰符 | 描述 |
|---|---|
| i | 不区分大小写:匹配时忽略大小写。 |
| m | 多行模式:点 (.) 元字符匹配换行符。^ 和 $ 匹配行的开始/结束。 |
| x | 扩展模式:忽略空白字符(除非被转义或在字符类中),并允许在 regex 中使用注释 (# ...)。对复杂模式很有用。 |
| o | 一次:只在 regex 字面量首次评估时执行一次 #{...} 插值。在现代 Ruby 中较不常见,因为性能已有所改进。 |
Ruby 的默认字符串编码是 UTF-8,regexes 可以正确处理 Unicode 字符。除非处理特定编码的旧数据,否则现代 Ruby 通常不需要用于特定编码的旧修饰符(如 u, e, s, n)。
正则表达式模式:
Section titled “正则表达式模式:”大多数字符匹配它们本身。特殊的元字符,如 . * + ? ^ $ ( ) [ ] { } | \ 具有特定含义,如果你想按字面意思匹配它们,必须用反斜杠 (\) 转义。
下表列出了常见的 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 并为其分配一个名称。通过 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
Section titled “常见陷阱:^ 和 $ 与 \A 和 \z”在匹配整个字符串时,使用 \A(字符串开始)和 \z(字符串结束)通常比使用 ^ 和 $ 更安全。如果使用了 /m 修饰符,^ 和 $ 可以匹配多行字符串中的行开始/结束,这可能不是输入验证所需的行为。
命名捕获示例:
Section titled “命名捕获示例:”命名捕获使复杂 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: INFOMessage: Request processedDuration: 0.025Controller: UserControllerAction: index搜索与替换:sub 和 gsub
Section titled “搜索与替换:sub 和 gsub”String 类提供了 sub(替换第一个匹配项)和 gsub(替换所有匹配项)方法,用于使用正则表达式进行搜索和替换操作。它们的带感叹号版本(sub! 和 gsub!)会就地修改字符串。
#!/usr/bin/env ruby
phone = "Call (123) 456-7890 or 987-654-3210 # Support lines"
# Remove commentscleaned_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 logicstandardized_phone = cleaned_phone.gsub(/\(?(\d{3})\)?[\s-]?(\d{3})[\s-]?(\d{4})/) do |match_text| # $1, $2, $3 引用当前匹配中的捕获组 "+#{$1}-#{$2}-#{$3}"endputs "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(查找第一个匹配项的索引)。
- Ruby
Regexp文档:https://ruby-doc.org/core/Regexp.html - Ruby
MatchData文档:https://ruby-doc.org/core/MatchData.html - 像 Rubular (https://rubular.com/) 这样的在线 regex 测试器非常适合实验。