从uft8解码的乱码字符无法反推回原始二进制,如果让你改造utf8编码,你会如何解决这个缺陷?

项目开发中遇到python3将字符串unicode转换为中文乱码,困惑之际,搜索到这篇文章。很好的解决了我的问题,下面我将文章内容摘要和个人实践摘录如下:

以下内容摘自:银河-蜗牛 - 知乎

考虑到现实世界中的兼容性,UTF-8 编码显然不需要改造。而实际上,也不需要去发明解决方案,因为 python 的开发者早就提出了一种方案:PEP 383,它提出了一个名为 surrogateescape 的编码错误处理程序来解决这个问题。不过我不清楚其它编程语言是如何处理的,所以不保证不同语言之间开箱即用的互操作性。

这个 PEP 提出的初衷是,在 posix 系统上,文件路径、环境变量、命令行参数都是以字节形式传递的,而在 Windows 上则是以 Unicode 的形式传递的。在 python3 中,需要统一用 Unicode 字符串来表示它们,这时这种不同平台之间的不一致就带来了问题:字节数据不一定能成功解码为 unicode。比如,os.listdir 返回了一个字节形式的文件名,实际上这个文件名不一定需要能解码为 unicode,我们只需要把它原样传递给 open 函数就可以打开正确的文件了。

于是,这就需要一种能让字节和 unicode 之间进行双向无损转换的方法。PEP 383 中提出的 surrogateescape 正是这个思路。它利用了 unicode 中为 UTF-16 设计的“代理码”(surrogate code),这些代理码占用了一部分 unicode 码位,包括从 U+D800 到 U+DFFF,但它们并不是真正的字符,只是 UTF-16 编码的一部分。“不是真正的字符”就意味着,正常的 unicode 字符串中是不会出现这些代理码的。这样,就提供了一个可以操作的空间:surrogateescape 会把那些 UTF-8 中无法解码的字节,一一映射到从 U+DC80U+DCFF 的这段范围内。因为 UTF-8 兼容 ASCII,所以出现的无法解码的字节一定就是在 0x800xFF 之间,都是 128 个,刚好足以建立起这样的一一映射。而一一映射就意味着可逆,在将 unicode 字符串编码回字节时,只需要把这些代理码重新映射回 0x800xFF 就可以了。另外可以看出,surrogateescape 的方案不仅支持 UTF-8,甚至可以支持任何兼容 ASCII 的编码。

不过,需要注意的是,surrogateescape 只是 python 内部对于字节和 unicode 的一种解决方案,那些由字节被 surrogateescape 解码得到的 unicode 字符串在 python 之外的环境是没有意义的,比如它不能用于直接渲染到屏幕上,也不能用其它方式解码。唯一有意义的解释方式就是把它用 surrogateescape 再转回字节,然后使用它的字节形式。指望用它来完成“乱码显示在屏幕上,鼠标复制下来粘贴到别处又能重新编码为原始字节”是不现实的。

关于具体的内容,请参阅 PEP 原文。下面是一个例子:

>>> b = bytes(range(160, 168))
>>> b
b'\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7'
>>> s = b.decode("utf-8", errors="surrogateescape")
>>> s
'\udca0\udca1\udca2\udca3\udca4\udca5\udca6\udca7'
>>> s.encode("utf-8", errors="surrogateescape")
b'\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7'
>>> _ == b
True

个人实践:

 

 本文摘自:

从uft8解码的乱码字符无法反推回原始二进制,如果让你改造utf8编码,你会如何解决这个缺陷? - 知乎

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值