Ruby中如何捕获正则数组的所有匹配并优先返回精确匹配?
解决Ruby正则联合匹配优先精确匹配并收集所有结果的问题
你当前的问题出在Regexp.union的匹配顺序——原数组里的短模式(/Dan/i)排在前面,正则引擎会优先匹配到短结果,后面更长的模式就没机会被匹配。另外scan默认是非重叠全局匹配,没法直接拿到重叠的匹配结果(比如danny里的danny和dan)。
要实现你想要的效果,需要调整正则顺序,同时换一种方式收集所有可能的匹配:
步骤1:按匹配模式长度降序排序正则数组
把最长的正则放在最前面,让引擎优先尝试匹配更精确(更长)的模式:
match_array = [/Dan/i, /Danny/i, /Daniel/i] # 按正则的源字符串长度从长到短排序 sorted_matchers = match_array.sort_by { |re| -re.source.length } # 排序后顺序:[/Daniel/i, /Danny/i, /Dan/i]
步骤2:自定义方法收集所有匹配结果
直接用scan没法拿到重叠匹配,所以需要遍历字符串的每个起始位置,检查每个正则是否能匹配,最后去重并按长度排序:
def find_all_matches(str, matchers) matches = [] matchers.each do |re| # 遍历字符串的每个起始索引,尝试匹配当前正则 (0...str.length).each do |start_idx| if match_data = re.match(str, start_idx) matched_str = match_data[0] matches << matched_str unless matches.include?(matched_str) end end end # 按匹配结果长度降序排列,保证精确匹配在前 matches.uniq.sort_by { |m| -m.length } end
测试验证
运行以下代码,结果完全符合你的预期:
puts find_all_matches('dan', sorted_matchers).inspect # => ["dan"] puts find_all_matches('danny', sorted_matchers).inspect # => ["danny", "dan"] puts find_all_matches('daniel', sorted_matchers).inspect # => ["daniel", "dan"] puts find_all_matches('dannnniel', sorted_matchers).inspect # => ["dan"] puts find_all_matches('dannyel', sorted_matchers).inspect # => ["danny", "dan"]
补充说明
- 排序正则是为了让更长的、更精确的模式优先被匹配,避免短模式“抢占”匹配机会;
- 遍历每个起始索引是为了捕获所有重叠的匹配结果;
- 最后去重并按长度排序,确保结果里没有重复项,且精确匹配排在前面。
内容的提问来源于stack exchange,提问作者lumos
相关产品推荐
相关产品推荐

