如何将整数列表转换为精准匹配的动态Ruby正则表达式?
问题:Ruby中基于整数列表生成紧凑正则表达式
我想在Ruby里根据整数列表动态生成紧凑的正则表达式。比如输入列表:[1,2,3,4,5,6,7,9,10,11,12,13,14,15,17,18,19,20,21,22,23,25,26,27,28,29,30,31]
期望生成的正则是:/^([1-7]|9|1[0-5]|1[7-9]|2[0-3]|2[5-9]|3[0-1])$/
核心需求是把连续数字范围合并成紧凑格式:比如1-7转为[1-7]、10-15转为1[0-5],但我写的代码只会输出冗长的枚举形式,求修改优化方案,要满足:
- 合并连续范围为
[start-end]格式 - 妥善处理多位数的公共前缀(比如十位相同的连续数)
- 代码可读且高效
解决方案
核心分两步:先把整数列表分组为连续区间,再将每个区间转换成紧凑的正则片段,最后拼接成完整正则。
1. 分组连续整数区间
先实现一个方法,把输入的整数列表(自动去重排序)分割成连续的数字块,每个块用起始和结束值表示:
def group_consecutive_numbers(numbers) return [] if numbers.empty? sorted_numbers = numbers.uniq.sort groups = [[sorted_numbers.first]] sorted_numbers[1..].each do |num| last_group = groups.last if num == last_group.last + 1 last_group << num else groups << [num] end end groups.map { |g| [g.first, g.last] } end
2. 将区间转为紧凑正则片段
针对每个[start, end]区间,生成对应的正则字符串,重点处理同位数数字的公共前缀:
def range_to_regex_part(start_num, end_num) return start_num.to_s if start_num == end_num start_str = start_num.to_s end_str = end_num.to_s # 处理位数相同的连续数,提取公共前缀 if start_str.length == end_str.length prefix = "" (0...start_str.length).each do |i| if start_str[i] == end_str[i] prefix += start_str[i] else suffix_start = start_str[i..] suffix_end = end_str[i..] return "#{prefix}[#{suffix_start}-#{suffix_end}]" end end end # 位数不同的连续数(比如9-10)直接用范围格式 "#{start_num}-#{end_num}" end
3. 拼接成完整正则
把所有片段用|连接,加上边界匹配符生成最终正则:
def generate_compact_regex(numbers) return nil if numbers.empty? groups = group_consecutive_numbers(numbers) regex_parts = groups.map { |start, end_| range_to_regex_part(start, end_) } regex_str = "^(#{regex_parts.join('|')})$" Regexp.new(regex_str) end
测试验证
用你提供的输入列表测试:
numbers = [1,2,3,4,5,6,7,9,10,11,12,13,14,15,17,18,19,20,21,22,23,25,26,27,28,29,30,31] puts generate_compact_regex(numbers).inspect
输出结果完全符合预期:/^([1-7]|9|1[0-5]|1[7-9]|2[0-3]|2[5-9]|3[0-1])$/
代码说明
group_consecutive_numbers自动处理无序、重复的输入,确保分组逻辑准确range_to_regex_part优先提取多位数的公共前缀,只对差异部分做范围匹配,生成最紧凑的正则片段- 最终正则加上
^和$,确保匹配整个字符串,避免部分匹配的问题
内容的提问来源于stack exchange,提问作者Hussain Abbas Bani Wala
相关产品推荐
相关产品推荐

