Ruby 正则表达式完全指南:匹配、捕获与替换实战
Ruby 正则是语言一等公民。本文讲解 =~、match、命名捕获、gsub 替换、scan 提取等核心用法,附性能建议与"什么时候不该用正则"的清醒剂。
Ruby 正则写在 // 里就是对象:快速判断用 =~(返回位置或 nil)、要捕获组用 match(返回 MatchData)、批量提取用 scan、替换用 gsub。命名捕获 ?<name> 让复杂正则可读,能用字符串方法解决的问题别动用正则。
正则是一等公民
Ruby 的正则表达式基于 Onigmo 引擎,直接写在斜杠里就是 Regexp 对象:
pattern = /error|warn/i # i 忽略大小写
pattern.class # => Regexp
匹配判断的三种方式
'2026-10-08' =~ /\d{4}-\d{2}-\d{2}/ # => 0(匹配位置,不匹配返回 nil)
/\d+/.match?('abc123') # => true(只关心真假,最快)
'abc123'.match(/\d+/) # => #<MatchData "123">
=~:惯用判断式,配合$1、$2取捕获(线程不安全写法,遗留代码常见);match?:Ruby 2.4+ 引入,不需要捕获时性能最好;match:需要捕获组的正规途径。
捕获组与命名捕获
log = '[2026-10-08 12:00:00] ERROR disk full'
# 位置捕获
if m = log.match(/\[(.+?) (.+?)\] (\w+) (.+)/)
m[1] # => "2026-10-08"
m[3] # => "ERROR"
end
# 命名捕获:可读性完胜
pattern = /\[(?<date>.+?) (?<time>.+?)\] (?<level>\w+) (?<msg>.+)/
m = log.match(pattern)
m[:level] # => "ERROR"
命名捕获配合 named_captures 可直接转 Hash——解析日志行、结构化文本的利器。
常用模式与量词备忘
\d数字、\w单词字符、\s空白;大写为反义*零次或多次、+一次或多次、?零或一次、{n,m}区间- 默认贪婪匹配,量词后加
?变惰性(<.+?>匹配到最近闭合) ^$行首行尾,\A\z整个字符串首尾——校验用户输入务必用\A\z,^$在多行字符串里有绕过风险
scan 与 gsub:提取与替换
'a1b22c333'.scan(/\d+/) # => ["1", "22", "333"]
'电话:13800001111'.gsub(/\d{4}(?=\d{4})/, '****') # 手机号打码
# 块形式做计算式替换
'价格 100 元,税费 13 元'.gsub(/\d+/) { |n| n.to_i * 2 }
gsub 的块形式和 \\1 反向引用('\\1' 或 \k<name>)覆盖了从简单替换到模板转换的全部场景。
什么时候别用正则
正则不是万能钥匙,以下场景有更优解:
- 解析 JSON/CSV/HTML:用对应解析器(JSON、CSV、Nokogiri),正则解析嵌套结构在理论上行不通;
- 简单的开头/结尾判断:
start_with?、end_with?、include?更快更清晰; - 按固定分隔符拆分:
split足够; - 邮箱校验:别自己写(RFC 5322 的完整正则长达数千字符),用格式库或简单宽松匹配 + 发送验证邮件。
常见问题(FAQ)
Q:Ruby 正则支持前瞻后顾吗?
支持。(?=...) 正前瞻、(?!...) 负前瞻、(?<=...) 正后顾、(?<!...) 负后顾都可用,上面的手机号打码就是正前瞻实例。
Q:正则性能差怎么优化?
三个常见根因:嵌套量词导致的回溯爆炸((a+)+ 是灾难)、贪婪匹配在长文本上反复扫描、在循环内重复编译。对策:改写为原子组或占有量词、换惰性匹配、把正则提为常量复用。
Q:怎么处理多行文本匹配?
/m 修饰符让 . 匹配换行符;^ $ 默认就是按行锚定。提取跨行块(如日志堆栈)用 /\AERROR.*?^\]/m 这类组合,或干脆按行状态机处理——超长文本的正则往往不如逐行解析可靠。
参考文档
本文依据公开文档整理,示例需按项目依赖版本验证;不将文档中的性能描述视为本项目实测结果。