【问题标题】:How am I supposed to fix this utf-8 encoding error?我应该如何解决这个 utf-8 编码错误?
【发布时间】:2021-08-11 02:06:12
【问题描述】:

所以,我有像这样的非二进制字符串,它的编码被破坏了:

函数\xc3\xb3n est\xc3\xa1ndar 日期时间。约会时间。 now() retorna la fecha y hora actual.

这个字符串应该是这样的:

La función estándar 日期时间。约会时间。 now() retorna la fecha y hora actual.

在交互式控制台中,很容易修复:就像这样:

>>> b'La funci\xc3\xb3n est\xc3\xa1ndar datetime. datetime. now() retorna la fecha y hora actual.'.decode('utf-8')

这将输出正确解码的字符串。但是,在我尝试构建的脚本中,这个字符串就像您在第一个示例中看到的一样,但是是 unicode,而不是二进制。

我已经尝试了所有我能想到的技巧(除了硬编码一个等价字典并将它与replace() 一起使用,如果我能帮助它,我宁愿不这样做):我尝试过的最疯狂的事情是:

# Just to clarify the format of the broken strings, I declare this one here
broken_string = 'La funci\\xc3\\xb3n est\\xc3\\xa1ndar datetime. datetime. now() retorna la fecha y hora actual.'

match = re.findall(r'\\x[a-z0-9][a-z0-9]', broken_string)
for e in match:
    broken_string = str(broken_string.encode().replace( e.encode(), str(chr(int(e[-2:], 16))).encode() ))

好吧,实际上这个循环最终把字符串弄得更糟了:-$

这个可怕的火车残骸只是我能向你展示的最疯狂的想法。实际上,我已经尝试了很多东西,以至于我都不记得它们了。但你可能会在这里看到我的意图。

实际上很有趣,我似乎无法以一种优雅的方式解决这个问题,而无需硬编码这种风格的字典以在循环中与 str.replace() 一起使用:

dict_for_fix = {
    '\\xc3\\xb3' : b'\xc3\xb3'.decode('utf-8'),
    # I mean, I would have to brute-force hardcode lots of combinations this way...
}

这让我大吃一惊。没有比这更优雅的解决方案了吗?

【问题讨论】:

  • 如果你print(my_str[8:11]) 你会得到什么?
  • 我收到\xc。这说明了什么?
  • broken_string.encode('ascii').decode('unicode_escape').encode('latin1').decode('utf8')实际上可能是你想要的。
  • 有效!!它确实有效!谢谢...我已经浪费了两三个小时或更长时间试图弄清楚...您确实使我免于遭受更多小时的痛苦。我根本不知道“unicode_escape”的存在。 你可以继续发布它作为答案:-)
  • @metatoaster 我知道你在那里做了什么,这是很多不明显的步骤。绝对值得变成正确的答案。

标签: python python-3.x character-encoding python-3.6


【解决方案1】:

当试图解开具有双重编码序列的字符串时,该序列旨在成为转义序列(即\\而不是\),特殊的text encoding codec unicode_escape可用于将它们纠正回预期的实体用于进一步处理。但是,鉴于输入已经是 str 类型,需要将其转换为 bytes - 假设整个字符串具有完全有效的 ascii 代码点,这可能是初始转换的编解码器初始str 输入到bytes。如果str 中表示有标准的unicode 代码点,则可以使用utf8 编解码器,因为unicode_escape 序列不会影响这些代码点。例子:

>>> broken_string = 'La funci\\xc3\\xb3n est\\xc3\\xa1ndar datetime.'
>>> broken_string2 = 'La funci\\xc3\\xb3n estándar datetime.'
>>> broken_string.encode('ascii').decode('unicode_escape')
'La función estándar datetime.'
>>> broken_string2.encode('utf8').decode('unicode_escape')
'La función estándar datetime.'

假设unicode_escape 编解码器假定解码为latin1,则此中间字符串可以简单地使用latin1 编解码器解码后编码为bytes,然后再将其转回unicode str 类型通过utf8(或任何合适的目标)编解码器:

>>> broken_string.encode('ascii').decode('unicode_escape').encode('latin1').decode('utf8')
'La función estándar datetime.'
>>> broken_string2.encode('utf8').decode('unicode_escape').encode('latin1').decode('utf8')
'La función estándar datetime.'

根据要求,补充说明部分混乱的字符串。请注意,由于存在未转义的á 字符,尝试使用ascii 编解码器解码broken_string2 将不起作用。

>>> broken_string2.encode('ascii').decode('unicode_escape').encode('latin1').decode('utf8')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode character '\xe1' in position 21: ordinal not in range(128)

【讨论】:

  • 我刚刚注意到您的新示例之后的一些内容:如果我使用了您在问题评论中向我展示的组合(您未包含在最终答案中,但为了完整起见,我对其进行了编辑),如果我在broken_string2 上使用它,它会像UnicodeEncodeError: 'ascii' codec can't encode character '\xe1' in position 21: ordinal not in range(128) 一样崩溃。 如果您没有制定最终答案,我什至不会考虑部分(但不是完全)损坏的字符串。好点
  • @SebasSBM 是的,broken_string2 包含非 ascii 代码点 á - 因此它不能编码为 ascii。此外,我在测试时复制粘贴了错误的行,我打算在原始输入上编码为ascii(而不是latin1),以演示引言中所写的原始语句。
  • latin1 运作良好的原因在于它是 only 编码,它在 0-255 的代码点值和字节值之间具有 1:1 的对应关系。这是一个方便的技巧。
猜你喜欢
  • 1970-01-01
  • 2016-07-21
  • 2016-09-09
  • 1970-01-01
  • 2019-07-15
  • 2014-04-23
相关资源
最近更新 更多