解析OpenStreetMaps XML时正则格式化手机号异常排查
这种“单独测试正常,集成到流程就出问题”的bug真的太磨人了!我之前处理批量文本清洗时也踩过类似的坑,给你梳理几个最可能的原因和排查方向:
1. 函数状态残留/变量污染
如果你的清理函数依赖了全局变量、闭包变量,或者复用了带状态的正则对象,批量处理OSM XML时,前面的处理可能会修改这些状态,导致后续手机号处理出错。比如:
- 你在函数外部定义了正则表达式对象,且该对象带有计数、匹配位置等状态
- 替换逻辑里用到了全局的临时变量,每次调用没有重置
排查方法:
在清理函数内部打印关键变量的状态,或者把所有状态相关的逻辑移到函数内部(比如每次调用都重新编译正则)。
修复示例:
把正则编译放到函数内部,确保每次调用都是全新的匹配状态:
import re def clean_phone(phone): # 每次调用都重新初始化正则,避免状态残留 # 先提取所有数字 digits = re.findall(r'\d', phone) if len(digits) == 11 and digits[0] == '1': return f"+1 {' '.join([''.join(digits[1:4]), ''.join(digits[4:7]), ''.join(digits[7:11])])}" # 其他长度的号码处理逻辑... return phone
2. 原始文本存在隐藏字符
OSM XML中的手机号可能包含看不见的控制字符(比如零宽空格、制表符、非打印ASCII字符),你单独测试时输入的是干净字符串,但批量处理时原始数据有干扰字符,导致正则匹配异常。
排查方法:
把出错的手机号从XML中提取后,用repr()打印查看真实内容:
problem_phone = "+1 (720) 406-9696" # 替换成从XML中提取的真实值 print(repr(problem_phone))
如果输出里有\u200b(零宽空格)、\t、\x00这类字符,就说明是隐藏字符搞的鬼。
修复方法:
在清理前先过滤所有非必要字符:
def clean_phone(phone): # 先过滤掉所有非数字、非允许符号的字符 cleaned = re.sub(r'[^\d\+\(\)\-\s]', '', phone) # 再执行后续的格式整理逻辑...
3. 分步替换的正则逻辑顺序问题
如果你的清理是分多步替换(比如先去括号、再替换连字符、再拆分空格),可能某一步的正则匹配在批量场景下没有覆盖所有情况,导致后续步骤出错。比如你用re.sub(r'\(', '', phone)时,可能因为前面有隐藏字符,导致括号没被正确替换。
更可靠的替代方案:
跳过分步替换,直接提取所有数字再重新拼接格式。这种方法完全不受原始符号干扰:
def clean_phone(phone): digits = re.findall(r'\d', phone) if len(digits) == 11: if digits[0] == '1': # 北美号码格式 return f"+1 {' '.join([''.join(digits[1:4]), ''.join(digits[4:7]), ''.join(digits[7:11])])}" else: # 其他11位号码处理 return f"+{digits[0]} {' '.join([''.join(digits[1:4]), ''.join(digits[4:7]), ''.join(digits[7:11])])}" elif len(digits) == 10: # 补上前缀1 return f"+1 {' '.join([''.join(digits[:3]), ''.join(digits[3:6]), ''.join(digits[6:10])])}" # 不符合长度的返回原始值或做其他处理 return phone
4. Jupyter环境的缓存问题
有时候Jupyter会缓存旧版本的函数或变量,你在新单元格测试的是修改后的函数,但处理XML的代码调用的还是旧版本的函数。
排查方法:
- 重新运行定义清理函数的单元格
- 重启Jupyter内核后重新运行所有代码
- 可以用
%autoreload魔法命令自动重载模块(需要先运行%load_ext autoreload和%autoreload 2)
内容的提问来源于stack exchange,提问作者elne2504

