解码JSON字符串中的UTF-8编码

11

我有一个JSON文件,其中包含以下编码的字符串:

"sender_name": "Horn\u00c3\u00adkov\u00c3\u00a1",

我尝试使用json模块解析此文件。但是,我无法正确地解码此字符串。

在使用.load()方法解码JSON后,我得到的是'HornÃ\xadková'。该字符串应正确解码为'Horníková'

我阅读了JSON规范,并理解了\u之后应该有4个十六进制数字,指定字符的Unicode编号。但似乎在这个JSON文件中,UTF-8编码的字节被存储为\u序列。

这是什么类型的编码方式,如何在Python 3中正确解析它?

根据规范,这种类型的JSON文件是否有效?


你在打开传递给 json.load() 的文件时是否指定了编码? - Will Keeling
@Will Keeling 是的,我尝试了指定编码为utf-8,也尝试了不指定编码 - 两个选项都给出了相同的结果。 - Matej Kormuth
4个回答

10

您的文本已经编码,您需要使用b前缀在字符串中告诉Python,但由于您正在使用json并且输入需要是字符串,因此您必须手动解码已编码的文本。由于您的输入不是字节,您可以使用'raw_unicode_escape'编码将字符串转换为字节而不进行编码,并防止open方法使用其自己的默认编码。然后,您可以简单地使用上述方法来获得所需的结果。

请注意,由于您需要进行编码和解码,因此您需要读取文件内容并对加载的字符串执行编码,然后应该使用json.loads()而不是json.load()

In [168]: with open('test.json', encoding='raw_unicode_escape') as f:
     ...:     d = json.loads(f.read().encode('raw_unicode_escape').decode())
     ...:     

In [169]: d
Out[169]: {'sender_name': 'Horníková'}

能否逐步完成此操作(即不必先读取整个文件,对其进行编码,然后将其传递给JSON解析器)? - Samuele Pilleri
@SamuelePilleri 这与JSON文件的本质相矛盾。然而,有一些努力正在进行中,以使在某些情况下这种可能性成为现实。Python默认不支持此功能。 - Mazdak
不太确定为什么(这不是递归下降解析器吗?),无论如何,我的意思是是否有一种逐步编码/解码流的方法,而不必首先将其加载到内存中。 - Samuele Pilleri
请查看此问题 - Samuele Pilleri

7

您正在阅读的JSON文件编写错误,从中解码的Unicode字符串将需要使用错误的编码重新编码,然后再使用正确的编码进行解码。

以下是一个示例:

#!python3
import json

# The bad JSON you have
bad_json = r'{"sender_name": "Horn\u00c3\u00adkov\u00c3\u00a1"}'
print('bad_json =',bad_json)

# The wanted result from json.loads()
wanted = {'sender_name':'Horníková'}

# What correctly written JSON should look like
good_json = json.dumps(wanted)
print('good_json =',good_json)

# What you get when loading the bad JSON.
got = json.loads(bad_json)
print('wanted =',wanted)
print('got =',got)

# How to correct the mojibake string
corrected_sender = got['sender_name'].encode('latin1').decode('utf8')
print('corrected_sender =',corrected_sender)

输出:

bad_json = {"sender_name": "Horn\u00c3\u00adkov\u00c3\u00a1"}
good_json = {"sender_name": "Horn\u00edkov\u00e1"}
wanted = {'sender_name': 'Horníková'}
got = {'sender_name': 'HornÃ\xadková'}
corrected_sender = Horníková

3
重新编码为字节,然后重新解码为文本。
>>> 'HornÃ\xadková'.encode('latin-1').decode('utf-8')
'Horníková'

根据规范,这种类型的JSON文件是否有效?
不是。
"字符串"是零个或多个Unicode字符的序列,用双引号括起来,使用反斜杠转义[强调添加]。 source "字符串"是用引号(U+0022)包装的Unicode代码点序列。[...]任何代码点都可以表示为十六进制转义序列[...]表示为一个六字符序列:反向实心斜杠,后跟小写字母u,后跟编码代码点的四个十六进制数字[强调添加]。 source UTF-8字节序列既不是Unicode字符也不是Unicode代码点。

3

我对JSON不够了解,无法确定这是否有效,但是你可以使用raw_unicode_escape编解码器来解析这些字符串:

>>> "Horn\u00c3\u00adkov\u00c3\u00a1".encode('raw_unicode_escape').decode('utf8')
'Horníková'

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接