别逐字符扫码点,那是排查手段,不是治理手段。系统性做法分三层:
1. 清洗:黑名单 + 白名单组合
先黑名单干掉已知脏字符,再白名单兜底。Python 侧:
import re, unicodedata
# 零宽类 + 各类空白 + 控制符
DIRTY = re.compile(r'[\u200b-\u200f\u202a-\u202e\u2060\ufeff\u00a0\u3000\u2028\u2029]')
def clean(s):
s = DIRTY.sub('', s)
s = re.sub(r'\s+', ' ', s).strip() # 连续空白压成一个半角空格
return s
注意 \s 在 Python 里已经包含 \u00a0、\u3000,但不含零宽字符,所以必须显式列。另外顺手做 NFC 归一化(unicodedata.normalize('NFC', s)),防止组合字符造成"看着一样码点不同"。
2. 校验:导入时就标行号,别等入库后
在解析 Excel 那一层就拦,返回结构化结果:
def validate(row_idx, addr):
raw = str(addr)
cleaned = clean(raw)
issues = []
if raw != cleaned:
bad = [f'U+{ord(c):04X}' for c in raw if c in DIRTY_CHARS]
issues.append(f'含隐藏字符 {bad}')
if not re.fullmatch(r'[\u4e00-\u9fa5a-zA-Z0-9\s\-#()()、,,.。]+', cleaned):
issues.append('含非白名单字符')
return {'row': row_idx, 'raw': raw, 'clean': cleaned, 'issues': issues}
关键是把原始值和清洗值都留下,脏数据不直接丢弃,而是落到"待人工确认"表,附带行号和码点列表。全量丢弃风险太大——有些字符可能是用户真意(比如地址里的 #)。
3. 入库:唯一索引 + 归一化列
加一列 addr_normalized(清洗后值),在它上面建唯一索引。这样即使清洗逻辑漏了,重复入库也会被数据库挡住。比对标准库时,两边都走同一套 clean() 函数,这是最容易踩的坑——只清洗了导入侧,标准库没洗,照样匹配不上。
排查残留: 清洗完跑一遍 Counter(c for c in text if not c.isprintable() or c in '\u00a0\u3000'),返回空就干净了。
Excel 侧还有个隐藏来源:从网页或 PDF 复制的单元格会带 \u202a 这类双向文本控制符,肉眼完全不可见,一并列进黑名单。
官方详细解决方案:https://cloud.tencent.com/product/lighthouse?Is=sdk-topnav... 展开详请