Python2.7与Python3.7中破折号编码转换差异问题求助
Python2与Python3编码差异原因及解决方案
差异原因
1. 字符串模型本质不同
- Python2中,
str是字节序列,unicode才是文本序列。调用str.encode(encoding)时,会先将字节串按系统默认编码解码为unicode,再编码为目标格式;默认错误处理策略是replace,遇到编码不支持的字符会自动替换为兼容字符(此处恰好替换为普通连字符-)。 - Python3中,
str是unicode文本序列,bytes是字节序列。调用str.encode(encoding)时直接将unicode编码为目标字节串,默认错误处理策略是strict,遇到编码表中不存在的字符直接抛出UnicodeEncodeError。
2. 具体场景的行为差异
- 对于
s.encode('iso8859_15').decode('iso8859_15'):- Python2:先把
s(字节串)解码为unicode字符U+2013(长破折号),编码为iso8859_15时因该字符不在编码表中,触发replace策略替换为U+002D(普通连字符),解码后得到u'-'。 - Python3:直接尝试编码
U+2013为iso8859_15,因编码表无此字符,触发strict策略抛出异常。
- Python2:先把
- 对于
s.encode('utf-8').decode('iso8859_15'):- Python2:先解码
s为unicode,编码为utf-8字节串后,用iso8859_15解码时的乱码被默认处理,最终巧合得到u'-'。 - Python3:将
U+2013编码为utf-8字节b'\xe2\x80\x93',用iso8859_15严格解码每个字节,得到乱码字符串'â\x80\x93'。
- Python2:先解码
解决方案
由于不能修改测试用例,需让Python3行为完全匹配Python2,以下是几种可行方法:
方法1:模拟Python2编码流程,指定错误处理为replace
直接在编码时显式指定errors='replace',还原Python2的自动替换逻辑:
s = '–' # 对应Python2的 s.encode('iso8859_15').decode('iso8859_15') result1 = s.encode('iso8859_15', errors='replace').decode('iso8859_15') # 对应Python2的 s.encode('utf-8').decode('iso8859_15') result2 = s.encode('utf-8').decode('iso8859_15', errors='replace')
方法2:精准映射目标字符
针对U+2013(长破折号)单独替换为普通连字符,既匹配Python2的结果,又不影响其他字符:
s = '–' # 替换长破折号为普通连字符 processed_s = s.replace('\u2013', '-') # 执行编码解码操作 result1 = processed_s.encode('iso8859_15').decode('iso8859_15') result2 = processed_s.encode('utf-8').decode('iso8859_15')
方法3:全局修改编码错误策略(不推荐)
通过codecs模块全局设置错误处理为replace,但可能影响其他代码的正常逻辑,谨慎使用:
import codecs # 将默认的strict错误处理替换为replace codecs.register_error('strict', codecs.replace_errors) s = '–' result1 = s.encode('iso8859_15').decode('iso8859_15') result2 = s.encode('utf-8').decode('iso8859_15')
内容的提问来源于stack exchange,提问作者Sachin Agrawal
相关产品推荐
相关产品推荐

