Python/Django中批量替换OCR返回Unicode转义序列失效问题排查
问题说明
由于业务需求,需要对OCR接口返回字符串中的类Unicode转义内容做替换处理:
- OCR接口返回的形如
\u017a的内容不是Unicode映射的单个字符,而是由\、u、对应十六进制字符组成的6个独立字符的字面序列 - 接口返回逻辑无法修改,只能在本地做字符串替换
最初采用逐个字符替换的写法可以正常生效,代码如下:
def recode(mystr): mystr = mystr.replace(r'\u0104', '\u0104') mystr = mystr.replace(r'\u017c', '\u017c') mystr = mystr.replace(r'\u0106' , '\u0106') ... ... mystr = mystr.replace(r'\u017a' , '\u017a') mystr = mystr.replace(r'\u017c' , '\u017c') return mystr
该写法冗余度高、可维护性差,因此尝试改为循环批量替换,但代码完全不生效,失效代码如下:
def recode(mystr): for foo in ['\u0106','\u0118','\u0141', ...... , '\u017a','\u017c']: mystr = mystr.replace(r'%s' % foo, foo) return mystr
核心疑问:
- 为什么逐个替换的写法可以正确匹配原始字面文本
- 循环写法中
foo为什么无法匹配到目标文本 - 两种写法的核心差异是什么
核心原因
两种写法的本质差异来自Python字符串的解析规则,以及原始字符串(r前缀)的生效边界:
- 逐个替换生效的原理
写法中作为匹配源的r'\u0104'是原始字符串字面量,Python解析代码阶段不会对其中的\u开头序列做Unicode转义,会完整保留6个独立字符的字面形态,和OCR返回的内容完全一致,因此可以正常匹配。作为替换目标的'\u0104'是普通字符串,会被解析为单个对应Unicode字符,替换逻辑符合预期。 - 循环替换失效的原因
写法存在两个根本错误:- 遍历列表中写的
'\u0106'是普通字符串,Python在加载代码、解析列表字面量的阶段,就已经把所有\u开头的转义序列解析成了单个Unicode字符,也就是说循环中的变量foo从一开始就是单个实际字符,完全不是需要匹配的6字符字面序列。 r'%s' % foo的写法对匹配没有任何帮助:r前缀仅对直接写在代码里的字符串字面量生效,不会改变字符串格式化后的结果。这里的r只作用于'%s'这两个字符组成的字面量,格式化插入的foo是已经解析完成的单个Unicode字符,最终生成的匹配串就是这个单字符,和OCR返回的6字符序列完全不匹配,自然替换失效。
- 遍历列表中写的
正确的批量替换实现
不需要手写每个替换逻辑,只需要维护需要处理的Unicode码十六进制列表,运行时拼接匹配串、生成目标字符即可,示例代码:
def recode(mystr): # 仅需维护所有需要处理的Unicode码十六进制后缀 target_codes = ['0104', '017c', '0106', '0118', '0141', '017a'] for code_hex in target_codes: # 拼接得到6字符的字面匹配序列 match_seq = f'\\u{code_hex}' # 生成对应的实际Unicode字符作为替换目标 target_char = chr(int(code_hex, 16)) mystr = mystr.replace(match_seq, target_char) return mystr
内容的提问来源于stack exchange,提问作者Wincij
相关产品推荐
相关产品推荐

