为什么json.dumps会用“\uxxxx”转义非ascii字符
Python 2 可以将纯 ascii 字节字符串和 Unicode 字符串混合在一起。
这可能是一个过早的优化。如果 Unicode 字符串在 Python 2 中主要包含 ASCII 范围内的字符,则它们可能需要比相应字节字符串多 2-4 倍的内存。
此外,即使在今天,如果 print(unicode_string) 在打印到 Windows 控制台时包含非 ascii 字符,它也可能很容易失败,除非安装了 win-unicode-console Python 包之类的东西。如果使用 C/POSIX 语言环境(init.d 服务的默认设置,ssh,cron 在许多情况下),即使在 Unix 上它也可能会失败(这意味着 ascii 字符编码。有C.UTF-8,但它并不总是可用并且您必须明确配置它)。这或许可以解释为什么在某些情况下您可能需要ensure_ascii=True。
JSON 格式是为 Unicode 文本定义的,因此严格来说 json.dumps() 应该总是返回一个 Unicode 字符串,但如果所有字符都在 ASCII 范围内,它可能会返回一个字节串(xml.etree.ElementTree 有类似的“优化”)。在某些情况下,Python 2 允许将仅 ascii 的字节字符串视为 Unicode 字符串,这令人困惑(允许隐式转换)。 Python 3 更严格(禁止隐式转换)。
可以使用纯 ASCII 字节字符串代替 Unicode 字符串(可能包含非 ASCII 字符)以节省内存和/或提高 Python 2 中的互操作性。
要禁用该行为,请使用json.dumps(obj, ensure_ascii=False)。
避免将 Unicode 字符串与其在 Python 源代码中作为 Python 字符串文字的 表示形式 或其在文件中作为 JSON 文本的表示形式混淆,这一点很重要。
JSON 格式允许转义任何字符,而不仅仅是 ASCII 范围之外的 Unicode 字符:
>>> import json
>>> json.loads(r'"\u0061"')
u'a'
>>> json.loads('"a"')
u'a'
不要将它与 Python 源代码中使用的 Python 字符串文字中的转义混淆。 u"\u00f8" 是 单个 Unicode 字符,但输出中的 "\u00f8" 是 八个 字符(在 Python 源代码 中,您可以将其正确为r'"\u00f8"' == '"\\u00f8"' == u'"\\u00f8"'(反斜杠在 Python literals 和 json 文本中都很特殊——双重转义可能发生)。JSON 中也没有 \x 转义:
>>> json.loads(r'"\x61"') # invalid JSON
Traceback (most recent call last):
...
ValueError: Invalid \escape: line 1 column 2 (char 1)
>>> r'"\x61"' # valid Python literal (6 characters)
'"\\x61"'
>>> '"\x61"' # valid Python literal with escape sequence (3 characters)
'"a"'
json.dumps() 的输出是一个 str,它在 Python 2 中是一个字节字符串。因此它不应该将字符转义为 \xhh 吗?
json.dumps(obj, ensure_ascii=True) 仅生成可打印的 ascii 字符,因此 print repr(json.dumps(u"\xf8")) 不会包含用于表示 (repr()) 不可打印字符(字节)的\xhh 转义。
\u 转义即使对于纯 ascii 输入也是必要的:
#!/usr/bin/env python2
import json
print json.dumps(map(unichr, range(128)))
输出
["\u0000", "\u0001", "\u0002", "\u0003", "\u0004", "\u0005", "\u0006", "\u0007",
"\b", "\t", "\n", "\u000b", "\f", "\r", "\u000e", "\u000f", "\u0010", "\u0011",
"\u0012", "\u0013", "\u0014", "\u0015", "\u0016", "\u0017", "\u0018", "\u0019",
"\u001a", "\u001b", "\u001c", "\u001d", "\u001e", "\u001f", " ", "!", "\"", "#",
"$", "%", "&", "'", "(", ")", "*", "+", ",", "-", ".", "/", "0", "1", "2", "3",
"4", "5", "6", "7", "8", "9", ":", ";", "<", "=", ">", "?", "@", "A", "B", "C",
"D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S",
"T", "U", "V", "W", "X", "Y", "Z", "[", "\\", "]", "^", "_", "`", "a", "b", "c",
"d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s",
"t", "u", "v", "w", "x", "y", "z", "{", "|", "}", "~", "\u007f"]
但这不是很混乱吗,因为 \uxxxx 是一个 unicode 字符,应该在 unicode 字符串中使用
\uxxxx 是 6 个字符,在某些情况下可能被解释为单个字符,例如,在 Python 源代码中 u"\uxxxx" 是一个 Python 文字,它在内存中使用 single 创建一个 Unicode 字符串Unicode 字符。但是如果你在 json 文本中看到 \uxxxx;如果加载它,六个字符可能代表单个Unicode字符(json.loads())。
此时你应该明白为什么len(json.loads('"\\\\"')) == 1了。