为什么 json.dumps 使用 "\uxxxx" 转义非 ascii 字符
Why does json.dumps escape non-ascii characters with "\uxxxx"
在Python2中,函数json.dumps()
将确保所有非ascii字符都转义为\uxxxx
。
但这不是很令人困惑吗,因为 \uxxxx
是一个 unicode 字符,应该在 unicode 字符串中使用。
json.dumps()
的输出是一个 str
,它是 Python 2 中的字节字符串。因此它不应该将字符转义为 \xhh
吗?
>>> unicode_string = u"\u00f8"
>>> print unicode_string
ø
>>> print json.dumps(unicode_string)
"\u00f8"
>>> unicode_string.encode("utf8")
'\xc3\xb8'
这正是重点。你得到一个字节字符串,而不是一个 Unicode 字符串。因此,需要对 Unicode 字符进行转义才能生存。 JSON 允许转义,因此提供了一种表示 Unicode 字符的安全方式。
"\u00f8"
中的\u
实际上并不是像\x
那样的转义序列。 \u
是文字 r'\u'
。但是这样的字节串很容易转换成Unicode。
演示:
s = "\u00f8"
u = s.decode('unicode-escape')
print repr(s), len(s), repr(u), len(u)
s = "\u2122"
u = s.decode('unicode-escape')
print repr(s), len(s), repr(u), len(u)
输出
'\u00f8' 6 u'\xf8' 1
'\u2122' 6 u'\u2122' 1
正如 J.F.Sebastian 在评论中提到的那样,在 Unicode 字符串中 \u00f8
是一个真正的转义码,即在 Python 3 字符串或 Python 中2 u"\u00f8"
字符串。另请注意他的其他言论!
Why does json.dumps escape non-ascii characters with “\uxxxx”
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()
应该总是 return Unicode 字符串,但如果所有字符都在ASCII 范围(xml.etree.ElementTree
具有类似的 "optimization")。令人困惑的是 Python 2 在某些情况下允许将纯 ascii 字节串视为 Unicode 字符串(允许隐式转换)。 Python 3 更严格(禁止隐式转换)。
可以使用仅 ASCII 字节串代替 Unicode 字符串(可能带有非 ASCII 字符)以节省内存 and/or 提高 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"
是 8 个字符(在 Python 源代码,你可以把它改成r'"\u00f8"' == '"\u00f8"' == u'"\u00f8"'
(反斜杠在Python 文字和json中都是特殊的text -- double escaping may happen). 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"'
The output of json.dumps() is a str, which is a byte string in Python 2. And thus shouldn't it escape characters as \xhh ?
json.dumps(obj, ensure_ascii=True)
只生成可打印的 ascii 字符,因此 print repr(json.dumps(u"\xf8"))
不会包含 \xhh
用于 表示 的转义字符(repr()
) 不可打印的字符(字节)。
\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"]
But isn't this quite confusing because \uxxxx is a unicode character and should be used inside a unicode string
\uxxxx
是 6 个字符,在某些上下文中可能被解释为单个字符,例如,在 Python 源代码中 u"\uxxxx"
是一个 Python 文字,它创建一个内存中的 Unicode 字符串,带有 单个 Unicode 字符。但是,如果您在 json 文本中看到 \uxxxx
; 6 个字符可能 表示 如果您加载它 (json.loads()
).
单个 Unicode 字符
至此,你应该明白为什么len(json.loads('"\\"')) == 1
.
在Python2中,函数json.dumps()
将确保所有非ascii字符都转义为\uxxxx
。
但这不是很令人困惑吗,因为 \uxxxx
是一个 unicode 字符,应该在 unicode 字符串中使用。
json.dumps()
的输出是一个 str
,它是 Python 2 中的字节字符串。因此它不应该将字符转义为 \xhh
吗?
>>> unicode_string = u"\u00f8"
>>> print unicode_string
ø
>>> print json.dumps(unicode_string)
"\u00f8"
>>> unicode_string.encode("utf8")
'\xc3\xb8'
这正是重点。你得到一个字节字符串,而不是一个 Unicode 字符串。因此,需要对 Unicode 字符进行转义才能生存。 JSON 允许转义,因此提供了一种表示 Unicode 字符的安全方式。
"\u00f8"
中的\u
实际上并不是像\x
那样的转义序列。 \u
是文字 r'\u'
。但是这样的字节串很容易转换成Unicode。
演示:
s = "\u00f8"
u = s.decode('unicode-escape')
print repr(s), len(s), repr(u), len(u)
s = "\u2122"
u = s.decode('unicode-escape')
print repr(s), len(s), repr(u), len(u)
输出
'\u00f8' 6 u'\xf8' 1
'\u2122' 6 u'\u2122' 1
正如 J.F.Sebastian 在评论中提到的那样,在 Unicode 字符串中 \u00f8
是一个真正的转义码,即在 Python 3 字符串或 Python 中2 u"\u00f8"
字符串。另请注意他的其他言论!
Why does json.dumps escape non-ascii characters with “\uxxxx”
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()
应该总是 return Unicode 字符串,但如果所有字符都在ASCII 范围(xml.etree.ElementTree
具有类似的 "optimization")。令人困惑的是 Python 2 在某些情况下允许将纯 ascii 字节串视为 Unicode 字符串(允许隐式转换)。 Python 3 更严格(禁止隐式转换)。
可以使用仅 ASCII 字节串代替 Unicode 字符串(可能带有非 ASCII 字符)以节省内存 and/or 提高 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"
是 8 个字符(在 Python 源代码,你可以把它改成r'"\u00f8"' == '"\u00f8"' == u'"\u00f8"'
(反斜杠在Python 文字和json中都是特殊的text -- double escaping may happen). 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"'
The output of json.dumps() is a str, which is a byte string in Python 2. And thus shouldn't it escape characters as \xhh ?
json.dumps(obj, ensure_ascii=True)
只生成可打印的 ascii 字符,因此 print repr(json.dumps(u"\xf8"))
不会包含 \xhh
用于 表示 的转义字符(repr()
) 不可打印的字符(字节)。
\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"]
But isn't this quite confusing because \uxxxx is a unicode character and should be used inside a unicode string
\uxxxx
是 6 个字符,在某些上下文中可能被解释为单个字符,例如,在 Python 源代码中 u"\uxxxx"
是一个 Python 文字,它创建一个内存中的 Unicode 字符串,带有 单个 Unicode 字符。但是,如果您在 json 文本中看到 \uxxxx
; 6 个字符可能 表示 如果您加载它 (json.loads()
).
至此,你应该明白为什么len(json.loads('"\\"')) == 1
.