Ruby 正则表达式应用攻略
正则表达式基础
在 Ruby 中,正则表达式是一种强大的工具,用于在字符串中匹配、搜索和替换文本模式。正则表达式由普通字符(例如字母、数字和标点符号)和特殊字符(称为元字符)组成。这些元字符赋予了正则表达式强大的模式匹配能力。
字符匹配
- 普通字符:最简单的正则表达式就是由普通字符组成,它们匹配自身。例如,正则表达式
ruby
会匹配字符串中出现的 “ruby”。
string = "I love ruby"
if string =~ /ruby/
puts "匹配成功"
else
puts "匹配失败"
end
在上述代码中,=~
操作符用于在字符串中搜索正则表达式。如果找到匹配项,它返回匹配的起始位置;如果没有找到,返回 -1
。
- 字符类:字符类允许匹配一组字符中的任意一个。例如,
[aeiou]
匹配任何一个元音字母。
string = "apple"
if string =~ /[aeiou]/
puts "包含元音字母"
else
puts "不包含元音字母"
end
方括号内的字符是或的关系,只要字符串中包含其中任意一个字符就算匹配成功。
- 否定字符类:使用
^
作为字符类的第一个字符,就可以创建否定字符类。例如,[^aeiou]
匹配任何一个非元音字母。
string = "fly"
if string =~ /[^aeiou]/
puts "包含非元音字母"
else
puts "不包含非元音字母"
end
重复匹配
*
元字符:表示前面的字符可以出现 0 次或多次。例如,ab*
可以匹配 “a”、“ab”、“abb” 等。
strings = ["a", "ab", "abb"]
strings.each do |str|
if str =~ /ab*/
puts "#{str} 匹配成功"
else
puts "#{str} 匹配失败"
end
end
+
元字符:表示前面的字符可以出现 1 次或多次。例如,ab+
可以匹配 “ab”、“abb”,但不能匹配 “a”。
strings = ["a", "ab", "abb"]
strings.each do |str|
if str =~ /ab+/
puts "#{str} 匹配成功"
else
puts "#{str} 匹配失败"
end
end
?
元字符:表示前面的字符可以出现 0 次或 1 次。例如,ab?
可以匹配 “a” 或 “ab”。
strings = ["a", "ab"]
strings.each do |str|
if str =~ /ab?/
puts "#{str} 匹配成功"
else
puts "#{str} 匹配失败"
end
end
- 重复次数限定:可以使用
{n}
表示前面的字符恰好出现 n 次,{n,}
表示前面的字符至少出现 n 次,{n,m}
表示前面的字符出现 n 到 m 次。例如,a{3}
匹配 “aaa”,a{3,}
匹配 “aaa” 及更多个 “a”,a{3,5}
匹配 “aaa”、“aaaa”、“aaaaa”。
strings = ["aa", "aaa", "aaaa"]
strings.each do |str|
if str =~ /a{3,}/
puts "#{str} 匹配成功"
else
puts "#{str} 匹配失败"
end
end
边界匹配
^
元字符:在正则表达式的开头使用^
表示匹配字符串的开头。例如,^ruby
只会匹配以 “ruby” 开头的字符串。
strings = ["ruby is great", "I love ruby"]
strings.each do |str|
if str =~ /^ruby/
puts "#{str} 匹配成功"
else
puts "#{str} 匹配失败"
end
end
$
元字符:在正则表达式的结尾使用$
表示匹配字符串的结尾。例如,ruby$
只会匹配以 “ruby” 结尾的字符串。
strings = ["I love ruby", "ruby is great"]
strings.each do |str|
if str =~ /ruby$/
puts "#{str} 匹配成功"
else
puts "#{str} 匹配失败"
end
end
\b
元字符:表示单词边界。例如,\bruby\b
只会匹配作为独立单词的 “ruby”,而不会匹配 “rubber” 中的 “ruby”。
strings = ["ruby", "rubber"]
strings.each do |str|
if str =~ /\bruby\b/
puts "#{str} 匹配成功"
else
puts "#{str} 匹配失败"
end
end
Ruby 中的正则表达式对象
在 Ruby 中,正则表达式可以用两种方式表示:字面量形式(例如 /pattern/
)和 Regexp
类的实例。
字面量形式
正则表达式字面量是由斜杠(/
)包围的模式。例如:
regex = /ruby/
string = "I love ruby"
if string =~ regex
puts "匹配成功"
end
这种方式简洁明了,适用于大多数简单的正则表达式场景。
Regexp 类
Regexp
类提供了更灵活的方式来创建和操作正则表达式。可以使用 Regexp.new
方法来创建一个 Regexp
对象。
pattern = "ruby"
regex = Regexp.new(pattern)
string = "I love ruby"
if string =~ regex
puts "匹配成功"
end
Regexp
类还支持一些选项,例如忽略大小写(i
)、多行匹配(m
)等。
regex = Regexp.new("ruby", Regexp::IGNORECASE)
string = "I love Ruby"
if string =~ regex
puts "匹配成功"
end
上述代码中,Regexp::IGNORECASE
选项使得正则表达式在匹配时忽略大小写。
捕获组
捕获组是正则表达式中用圆括号括起来的部分。它们允许我们在匹配成功后提取出特定的子字符串。
基本捕获组
例如,正则表达式 (\d{3})-(\d{2})-(\d{4})
可以用于匹配格式为 “XXX-XX-XXXX” 的日期,其中每个括号内的部分就是一个捕获组。
string = "123-45-6789"
if string =~ /(\d{3})-(\d{2})-(\d{4})/
puts "年: #{$1}, 月: #{$2}, 日: #{$3}"
end
在匹配成功后,$1
、$2
、$3
分别代表第一个、第二个和第三个捕获组匹配到的内容。
命名捕获组
从 Ruby 2.4 开始,支持命名捕获组。通过 (?<name>pattern)
的形式来定义命名捕获组。例如:
string = "123-45-6789"
if string =~ /(?<year>\d{3})-(?<month>\d{2})-(?<day>\d{4})/
puts "年: #{$~[:year]}, 月: #{$~[:month]}, 日: #{$~[:day]}"
end
这里 $~
是一个特殊的变量,它包含了匹配的结果,通过 $~[:name]
的形式可以访问命名捕获组匹配到的内容。
正则表达式的应用
字符串匹配
字符串匹配是正则表达式最常见的应用之一。除了前面介绍的 =~
操作符外,String
类还提供了一些其他方法来进行匹配。
match
方法:match
方法返回一个MatchData
对象,如果匹配成功;否则返回nil
。
string = "I love ruby"
match = string.match(/ruby/)
if match
puts "匹配成功,匹配内容: #{match[0]}"
else
puts "匹配失败"
end
MatchData
对象包含了关于匹配的详细信息,例如匹配的内容、捕获组的内容等。
scan
方法:scan
方法返回字符串中所有匹配的子字符串组成的数组。
string = "I love ruby, ruby is fun"
matches = string.scan(/ruby/)
puts matches.inspect
上述代码会输出 ["ruby", "ruby"]
,即字符串中所有匹配 “ruby” 的子字符串。
字符串替换
sub
方法:sub
方法用于替换字符串中第一个匹配的子字符串。
string = "I love ruby, ruby is fun"
new_string = string.sub(/ruby/, "Python")
puts new_string
上述代码会将字符串中第一个 “ruby” 替换为 “Python”,输出为 “I love Python, ruby is fun”。
gsub
方法:gsub
方法用于替换字符串中所有匹配的子字符串。
string = "I love ruby, ruby is fun"
new_string = string.gsub(/ruby/, "Python")
puts new_string
上述代码会将字符串中所有 “ruby” 替换为 “Python”,输出为 “I love Python, Python is fun”。
分割字符串
split
方法可以使用正则表达式作为分隔符来分割字符串。
string = "apple,banana;cherry"
parts = string.split(/[,\;]/)
puts parts.inspect
上述代码会使用逗号和分号作为分隔符来分割字符串,输出为 ["apple", "banana", "cherry"]
。
高级正则表达式技巧
零宽断言
零宽断言是一种特殊的正则表达式结构,它匹配一个位置而不是实际的字符。
- 正向先行断言:
(?=pattern)
表示匹配前面的位置,后面跟着pattern
。例如,ruby(?= is fun)
会匹配 “ruby”,但前提是 “ruby” 后面跟着 “ is fun”。
string = "ruby is fun"
if string =~ /ruby(?= is fun)/
puts "匹配成功"
end
- 负向先行断言:
(?!pattern)
表示匹配前面的位置,后面不跟着pattern
。例如,ruby(?! is fun)
会匹配 “ruby”,前提是 “ruby” 后面不跟着 “ is fun”。
strings = ["ruby is great", "ruby is fun"]
strings.each do |str|
if str =~ /ruby(?! is fun)/
puts "#{str} 匹配成功"
else
puts "#{str} 匹配失败"
end
end
- 正向回顾后发断言:
(?<=pattern)
表示匹配后面的位置,前面跟着pattern
。例如,(?<=I love )ruby
会匹配 “ruby”,前提是 “ruby” 前面是 “I love ”。
string = "I love ruby"
if string =~ /(?<=I love )ruby/
puts "匹配成功"
end
- 负向回顾后发断言:
(?<!pattern)
表示匹配后面的位置,前面不跟着pattern
。例如,(?<!I hate )ruby
会匹配 “ruby”,前提是 “ruby” 前面不是 “I hate ”。
strings = ["I love ruby", "I hate ruby"]
strings.each do |str|
if str =~ /(?<!I hate )ruby/
puts "#{str} 匹配成功"
else
puts "#{str} 匹配失败"
end
end
分支结构
分支结构使用 |
来表示或的关系。例如,ruby|python
会匹配 “ruby” 或 “python”。
strings = ["ruby", "python"]
strings.each do |str|
if str =~ /ruby|python/
puts "#{str} 匹配成功"
else
puts "#{str} 匹配失败"
end
end
正则表达式的嵌套
可以在正则表达式中嵌套其他正则表达式。例如,((\d{3})-(\d{2})-(\d{4}))
是一个嵌套的正则表达式,其中外层括号内包含了一个完整的日期格式匹配,而内层括号又定义了捕获组。
string = "123-45-6789"
if string =~ /((\d{3})-(\d{2})-(\d{4}))/
puts "完整日期: #{$1}, 年: #{$2}, 月: #{$3}, 日: #{$4}"
end
性能优化
在使用正则表达式时,性能是一个需要考虑的重要因素。以下是一些优化正则表达式性能的建议。
简化正则表达式
尽量简化正则表达式,避免使用不必要的复杂结构。例如,如果只需要匹配一个固定的字符串,直接使用普通字符匹配即可,而不要使用复杂的字符类或重复限定。
# 推荐
string = "ruby"
if string == "ruby"
puts "匹配成功"
end
# 不推荐
if string =~ /^r[au]by$/
puts "匹配成功"
end
预编译正则表达式
如果需要多次使用同一个正则表达式,预编译它可以提高性能。可以使用 Regexp.new
方法创建 Regexp
对象并进行预编译。
regex = Regexp.new("pattern")
strings = ["string1", "string2"]
strings.each do |str|
if str =~ regex
puts "#{str} 匹配成功"
end
end
避免过度使用捕获组
捕获组会增加正则表达式的计算量,尽量避免使用不必要的捕获组。如果只是为了分组而不是捕获内容,可以使用非捕获组 (?:pattern)
。
# 使用捕获组
string = "123-45-6789"
if string =~ /(\d{3})-(\d{2})-(\d{4})/
puts "年: #{$1}, 月: #{$2}, 日: #{$3}"
end
# 使用非捕获组
if string =~ /(?:\d{3})-(?:\d{2})-(?:\d{4})/
puts "匹配日期格式"
end
通过合理运用上述技巧和优化方法,可以在 Ruby 中更高效地使用正则表达式来处理字符串操作,无论是在数据验证、文本解析还是其他相关场景中,都能发挥出正则表达式强大的功能。同时,在实际应用中,要根据具体需求仔细编写和调试正则表达式,以确保其准确性和高效性。