Ответ 2
Почему json.dumps запускает символы не-ascii с помощью "\ uxxxx"
Python 2 может смешивать ascii-only bytestrings и строки Unicode вместе.
Это может быть преждевременная оптимизация. Строки Unicode могут требовать в 2-4 раза больше памяти, чем соответствующие байты, если они содержат символы в основном в диапазоне ASCII в Python 2.
Кроме того, даже сегодня print(unicode_string)
может легко выйти из строя, если он содержит символы не-ascii при печати в консоли Windows, если не установлено что-то вроде win-unicode-console
Python package. Он может потерпеть неудачу даже в Unix, если используется язык C/POSIX (по умолчанию для служб init.d
, ssh
, cron
) (что подразумевает кодировку символов ascii. Существует C.UTF-8
, но она не всегда доступна и вы должны настроить его явно). Это может объяснить, почему вам может понадобиться ensure_ascii=True
в некоторых случаях.
Формат JSON определен для текста в Юникоде, и, строго говоря, json.dumps()
всегда должен возвращать строку Юникода, но может возвращать байтовую строку, если все символы находятся в диапазоне ASCII (xml.etree.ElementTree
имеет аналогичную "оптимизацию" ). Сложно предположить, что Python 2 позволяет обрабатывать ascii-only bytestring как строку Unicode в некоторых случаях (допускаются неявные преобразования). Python 3 более строгий (неявные преобразования запрещены).
Вместо строк Unicode (с возможными символами, отличными от ASCII), вместо строк Unicode могут использоваться только ASCII-байты, чтобы сохранить память и/или улучшить взаимодействие в Python 2.
Чтобы отключить это поведение, используйте json.dumps(obj, ensure_ascii=False)
.
Важно не путать строку Unicode с ее представлением в исходном коде Python как строковый литерал Python или его представление в файле как текст JSON.
Формат JSON позволяет избежать любого символа, а не только символов Unicode вне диапазона ASCII:
>>> import json
>>> json.loads(r'"\u0061"')
u'a'
>>> json.loads('"a"')
u'a'
Не путайте его с экранами в строковых литералах Python, используемых в исходном коде Python. u"\u00f8"
- единственный символ Юникода, но "\u00f8"
в выводе - восемь символов (в исходном коде Python вы можете использовать его как r'"\u00f8"' == '"\\u00f8"' == u'"\\u00f8"'
(обратная косая черта является специальной как в литералах Python, так и в тексте json - двойное экранирование может случиться). В JSON нет \x
escape-кодов:
>>> 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"))
не будет содержать \xhh
экранов, которые используются для представления (repr()
) непечатаемых символов (байтов).
\u
escape файлы могут понадобиться даже для ввода только 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
\uxxxx
- это 6 символов, которые могут быть интерпретированы как один символ в некоторых контекстах, например, в исходном коде Python u"\uxxxx"
является литералом Python, который создает строку Unicode в памяти с одним символом Unicode. Но если вы видите \uxxxx
в json-тексте; это шесть символов, которые могут представлять один символ Юникода, если вы его загрузите (json.loads()
).
На этом этапе вы должны понять, почему len(json.loads('"\\\\"')) == 1
.