使用 json.loads() 保留 Unicode 字符,或者在执行 json.dumps() 时将它们转换回 Uni



>我有一个 json 文件,其中包含 unicode 字符u003cu003e。当使用 json.load(( 加载文件时,这些字符将转换为<>。请考虑以下实验:

d = json.loads('"Foo u003cfoo@bar.netu003e"')

然后打印如下:

'Foo <foo@bar.net>'

假设我需要将其转储回文件,并且需要将字符<>转换回u003cu003e。我目前正在使用f.write(json.dumps(d))但这似乎不起作用。

我已经搜索了几个小时,但就是无法弄清楚。

好吧,在这里了解Python解释器正在做什么会很有用。

当解释器找到字符串文本的开头时

在您的源代码中,您有以下一段文本:

'"Foo u003cfoo@bar.netu003e"'

当解析器找到第一个字符'时,它得出结论:"这是一个字符串文字!在我找到下一个'之前,我应该获取所有字符并将其放在列表中,以用作字符串。因此,假设它在内存中创建以下列表:

[]

然后它找到下一个字符,".由于字符串文本不是关闭的(因为没有找到'(,它会将其添加到列表中。与计算机中的所有内容一样,字符表示为数字。该数字是其 Unicode 点,对于",代码点为 34:

[ 34 ]
#  "

它对下一个字符执行相同的操作,将它们的代码点放在列表中:

[ 34   70  111  111   32 ]
#  "    F    o    o       

源代码中的u字符

现在,解释器找到字符。但这根本不是常见的字符!对于解释者来说,这意味着接下来的字符不是指他们自己,而是应该被解释。因此,口译员不会添加到列表中,而是让下一位口译员了解应该做什么。这就是为什么您的结果没有的原因。

下一个字符是u。因为它是以为前缀的,解释器不会将其插入到列表中。相反,u对被解释为获取接下来的四个字符的命令,将它们转换为十六进制数。这就是为什么您的结果中没有u的原因。

六个字符如何变成一个

接下来的四个字符是003c。它们形成0x3C十六进制数,即十进制形式的 60。因此,它被添加到列表中:

[ 34   70  111  111   32   60 ]
#  "    F    o    o         <

好吧,60 在 Unicode 中<这就是为什么你的结果有<这就是为什么当程序运行时,六个字符(u003c(实际上只代表一个(>(。

如何得到你想要的

当然,您可能希望在结果字符串中包含字符u等。如果是这样,Python 会给你一些选项,最简单的一个是原始字符串文字。为此,您只需要在字符串文字前面加上r前缀,如下所示:

r'"Foo u003cfoo@bar.netu003e"'

当解释器在源代码中对r进行修正,然后加上引号(如'(时,它知道它是一个字符串文字,但这个字符串文字根本没有解释。它里面的所有内容都将按照在源代码中键入的内容使用。这带来了类似于您似乎想要的结果:

>>> print('"Foo u003cfoo@bar.netu003e"')
"Foo <foo@bar.net>"
>>> print(r'"Foo u003cfoo@bar.netu003e"')
"Foo u003cfoo@bar.netu003e"

小心你的愿望

但请注意,这些字符串是完全不同的!甚至它们的大小也大不相同,因为第二个有更多的字符:

>>> len('"Foo u003cfoo@bar.netu003e"')
19
>>> len(r'"Foo u003cfoo@bar.netu003e"')
29

现在,我不得不说,你可能不想在这里有一个原始字符串。您可能只想用 Unicode 点表示字符串,但它也回避了为什么的问题。无论如何,现在由您决定您想要什么:)