如何在Python中去除重叠号码范围中的重复项?
号码范围去重展开问题与解决方案
问题描述
我有一个包含号码范围的列表,需要将每个范围展开为单个号码并关联对应标签(country、region、PBX,存在则保留),要求无重复项,出现重复时保留最小范围的标签。例如重叠范围976431[000-799]、9764317[00-99]、97643179[0-9],应处理为976431[000-699]、9764317[00-89]、97643179[0-9]以避免重复。但当前使用的Python脚本会生成重复项(如号码976431799出现三次),原脚本代码如下:
import numpy as np MYRANGES = [ {'Name': 'list1', 'Prefix': '976431[000-799]', 'Tag': 'Country=cz;Region=eu;'}, {'Name': 'list2', 'Prefix': '9764317[00-99]', 'Tag': 'Country=cz;Region=eu;Pbx=freepbx;'}, {'Name': 'list3', 'Prefix': '97643179[0-9]', 'Tag': 'Country=cz;Region=eu;Pbx=asterisk;'}, ] def ddi2csv(myranges): for myrange in myranges: prefixrange = myrange['Prefix'].replace(']', '').split('[')[0] suffixrange = myrange['Prefix'].replace(']', '').split('[')[1].split('-') for digit in np.arange(int(suffixrange[0]), int(suffixrange[1]) + 1): suffix = f"{digit:{str(0) + str(len(suffixrange[0]))}}" print( str(myrange['Name'].lower()) + "_" + str(suffix), prefixrange + suffix, myrange['Tag'] ) ddi2csv(MYRANGES)
解决思路与修改后代码
核心逻辑是先处理范围重叠冲突,保留最具体(长度最长)的范围标签,再展开号码,从根源避免重复生成。具体步骤:
- 将所有范围解析为完整的数字区间(起始、结束号码),同时保留标签、前缀等信息
- 按号码总长度从长到短排序,长度越长的范围优先级越高(越具体)
- 用高优先级范围切割低优先级范围,移除重叠部分
- 对处理后的无重叠范围进行展开输出
修改后的代码:
def parse_range(prefix_str): # 解析前缀字符串为完整的起始、结束数字,以及后缀长度、前缀 prefix, suffix_part = prefix_str.replace(']', '').split('[') start_suffix, end_suffix = suffix_part.split('-') suffix_len = len(start_suffix) start_num = int(f"{prefix}{start_suffix}") end_num = int(f"{prefix}{end_suffix}") return start_num, end_num, suffix_len, prefix def resolve_overlaps(ranges): # 解析所有范围为统一格式的区间对象 parsed_ranges = [] for item in ranges: start, end, suffix_len, prefix = parse_range(item['Prefix']) parsed_ranges.append({ 'start': start, 'end': end, 'tag': item['Tag'], 'name': item['Name'], 'suffix_len': suffix_len, 'prefix': prefix }) # 按号码总长度降序排序,优先保留更具体的范围 parsed_ranges.sort(key=lambda x: len(str(x['start'])), reverse=True) resolved = [] for current in parsed_ranges: curr_start = current['start'] curr_end = current['end'] # 检查当前范围与已保留范围的重叠,切割出不重叠部分 for existing in resolved.copy(): # 当前范围完全被现有范围包含,直接跳过 if curr_start >= existing['start'] and curr_end <= existing['end']: curr_start = None break # 部分重叠,切割当前范围 elif curr_start <= existing['end'] and curr_end >= existing['start']: if curr_start < existing['start']: # 保留当前范围的前半段不重叠部分 resolved.append({ 'start': curr_start, 'end': existing['start'] - 1, 'tag': current['tag'], 'name': current['name'], 'suffix_len': current['suffix_len'], 'prefix': current['prefix'] }) curr_start = existing['end'] + 1 if curr_start > curr_end: break # 若还有剩余不重叠部分,加入结果列表 if curr_start is not None and curr_start <= curr_end: resolved.append({ 'start': curr_start, 'end': curr_end, 'tag': current['tag'], 'name': current['name'], 'suffix_len': current['suffix_len'], 'prefix': current['prefix'] }) # 按起始号码升序排序,方便输出顺序更合理 resolved.sort(key=lambda x: x['start']) return resolved def ddi2csv(myranges): resolved_ranges = resolve_overlaps(myranges) for rng in resolved_ranges: # 生成后缀的格式化字符串,确保补零到指定长度 suffix_format = f"0{rng['suffix_len']}d" for num in range(rng['start'], rng['end'] + 1): # 提取后缀部分并格式化 suffix = str(num)[len(rng['prefix']):] suffix = format(int(suffix), suffix_format) print( f"{rng['name'].lower()}_{suffix}", f"{rng['prefix']}{suffix}", rng['tag'] ) # 测试数据 MYRANGES = [ {'Name': 'list1', 'Prefix': '976431[000-799]', 'Tag': 'Country=cz;Region=eu;'}, {'Name': 'list2', 'Prefix': '9764317[00-99]', 'Tag': 'Country=cz;Region=eu;Pbx=freepbx;'}, {'Name': 'list3', 'Prefix': '97643179[0-9]', 'Tag': 'Country=cz;Region=eu;Pbx=asterisk;'}, ] ddi2csv(MYRANGES)
代码说明
parse_range:将带范围的前缀字符串转换为可计算的数字区间,为后续重叠处理打基础resolve_overlaps:核心去重逻辑,通过优先级排序和范围切割,确保每个号码只属于最具体的范围ddi2csv:对处理后的无重叠范围进行展开,输出符合要求的号码、名称后缀和标签
内容的提问来源于stack exchange,提问作者user7478971
相关产品推荐
相关产品推荐

