Збереження текстів utf-8 у json.dumps як UTF8, а не як послідовність переходу \ u


473

зразок коду:

>>> import json
>>> json_string = json.dumps("ברי צקלה")
>>> print json_string
"\u05d1\u05e8\u05d9 \u05e6\u05e7\u05dc\u05d4"

Проблема: це не читабельно для людини. Мої (розумні) користувачі хочуть перевірити або навіть редагувати текстові файли за допомогою відвалів JSON (і я краще не використовую XML).

Чи є спосіб серіалізувати об'єкти в рядки UTF-8 JSON (замість \uXXXX)?


9
+ для ברי צקלה :)))
rubmz

Відповіді:


640

Використовуйте ensure_ascii=Falseперемикач на json.dumps(), а потім кодуйте значення UTF-8 вручну:

>>> json_string = json.dumps("ברי צקלה", ensure_ascii=False).encode('utf8')
>>> json_string
b'"\xd7\x91\xd7\xa8\xd7\x99 \xd7\xa6\xd7\xa7\xd7\x9c\xd7\x94"'
>>> print(json_string.decode())
"ברי צקלה"

Якщо ви пишете у файл, просто скористайтеся json.dump()і залиште його об’єкту файлу для кодування:

with open('filename', 'w', encoding='utf8') as json_file:
    json.dump("ברי צקלה", json_file, ensure_ascii=False)

Застереження для Python 2

Для Python 2 слід врахувати ще кілька застережень. Якщо ви пишете це у файл, ви можете використовувати io.open()замість того, open()щоб створити об’єкт файлу, який кодує значення Unicode для вас під час написання, а потім використовувати json.dump()замість цього, щоб записати у цей файл:

with io.open('filename', 'w', encoding='utf8') as json_file:
    json.dump(u"ברי צקלה", json_file, ensure_ascii=False)

Зверніть увагу , що існує помилка в jsonмодулі , де ensure_ascii=Falseпрапор може виробляти суміш з unicodeі strоб'єктів. Вирішення для Python 2 тоді:

with io.open('filename', 'w', encoding='utf8') as json_file:
    data = json.dumps(u"ברי צקלה", ensure_ascii=False)
    # unicode(data) auto-decodes data to unicode if str
    json_file.write(unicode(data))

У Python 2, використовуючи рядки байтів (тип str), закодовані до UTF-8, обов’язково встановіть також encodingключове слово:

>>> d={ 1: "ברי צקלה", 2: u"ברי צקלה" }
>>> d
{1: '\xd7\x91\xd7\xa8\xd7\x99 \xd7\xa6\xd7\xa7\xd7\x9c\xd7\x94', 2: u'\u05d1\u05e8\u05d9 \u05e6\u05e7\u05dc\u05d4'}

>>> s=json.dumps(d, ensure_ascii=False, encoding='utf8')
>>> s
u'{"1": "\u05d1\u05e8\u05d9 \u05e6\u05e7\u05dc\u05d4", "2": "\u05d1\u05e8\u05d9 \u05e6\u05e7\u05dc\u05d4"}'
>>> json.loads(s)['1']
u'\u05d1\u05e8\u05d9 \u05e6\u05e7\u05dc\u05d4'
>>> json.loads(s)['2']
u'\u05d1\u05e8\u05d9 \u05e6\u05e7\u05dc\u05d4'
>>> print json.loads(s)['1']
ברי צקלה
>>> print json.loads(s)['2']
ברי צקלה

72

Записати у файл

import codecs
import json

with codecs.open('your_file.txt', 'w', encoding='utf-8') as f:
    json.dump({"message":"xin chào việt nam"}, f, ensure_ascii=False)

Для друку в stdout

import json
print(json.dumps({"message":"xin chào việt nam"}, ensure_ascii=False))

1
SyntaxError: Символ, який не належить до ASCII '\ xc3' у файлі json-utf8.py у рядку 5, але кодування не оголошено; см python.org/dev/peps/pep-0263 подробиці
Alex

Дякую! Я не розумів, що це так просто. Вам потрібно бути обережним лише в тому випадку, якщо дані, які ви перетворюєте в json, не є довіреними користувачами.
Карім Сонбол

@Alex Ви зрозуміли, як уникнути цього питання?
Ярмарок Габріеля

@Gabriel чесно кажучи, не пам'ятаю. Не було настільки важливим, щоб відкласти фрагмент убік :(
Алекс

Мені працювали лише користуючись codecsбібліотекою. Дякую!
igorkf

29

ОНОВЛЕННЯ: Це неправильна відповідь, але все ж корисно зрозуміти, чому це неправильно. Дивіться коментарі.

Як щодо unicode-escape?

>>> d = {1: "ברי צקלה", 2: u"ברי צקלה"}
>>> json_str = json.dumps(d).decode('unicode-escape').encode('utf8')
>>> print json_str
{"1": "ברי צקלה", "2": "ברי צקלה"}

9
unicode-escapeне потрібно: ви можете використовувати це json.dumps(d, ensure_ascii=False).encode('utf8')замість цього. І не гарантується, що json використовує абсолютно ті самі правила, що і unicode-escapeкодек у Python, у всіх випадках, тобто результат може бути або не бути однаковим у деяких кутових випадках. Низький голос призначений для непотрібного та, можливо, неправильного перетворення. Непов'язане: print json_strпрацює лише для PYTHONIOENCODINGлокалів utf8 або якщо envvar вказує тут utf8 (замість цього надрукуйте Unicode).
jfs

3
Ще одне питання: будь-які подвійні лапки в рядкових значеннях втратять свою втечу, тому це призведе до зламаного виходу JSON .
Martijn Pieters

помилка в Python3: AttributeError: 'str' об’єкт не має атрибута 'декодування'
Gank

1
Unicode-escape прекрасно працює! Я би прийняв цю відповідь як правильну.
Робітник

@jfs Ні, json.dumps(d, ensure_ascii=False).encode('utf8')не працює, принаймні для мене. Я отримую UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position ...-помилка. Однак unicode-escapeваріант працює чудово.
turingtest

24

Пітерсон Пітон 2 вирішує проблему на крайньому корпусі:

d = {u'keyword': u'bad credit  \xe7redit cards'}
with io.open('filename', 'w', encoding='utf8') as json_file:
    data = json.dumps(d, ensure_ascii=False).decode('utf8')
    try:
        json_file.write(data)
    except TypeError:
        # Decode data to Unicode first
        json_file.write(data.decode('utf8'))

UnicodeEncodeError: 'ascii' codec can't encode character u'\xe7' in position 25: ordinal not in range(128)

Він зазнав збоїв у частині .decode ('utf8') рядка 3. Я вирішив проблему, зробивши програму значно спрощеною, уникаючи цього кроку, а також спеціального корпусу ascii:

with io.open('filename', 'w', encoding='utf8') as json_file:
  data = json.dumps(d, ensure_ascii=False, encoding='utf8')
  json_file.write(unicode(data))

cat filename
{"keyword": "bad credit  çredit cards"}

2
"Крайовий випадок" був просто німою неперевіреною помилкою з мого боку. Ваш unicode(data)підхід є кращим варіантом, а не використанням винятків. Зауважте, що encoding='utf8'аргумент ключового слова не має нічого спільного з результатом, який json.dumps()видає; він використовується для декодування strвводу, який отримує функція.
Martijn Pieters

2
@MartijnPieters: або простіше: open('filename', 'wb').write(json.dumps(d, ensure_ascii=False).encode('utf8'))Він працює, dumpsповертає (лише ascii) str або об'єкт unicode.
jfs

@JFSebastian: право, тому що str.encode('utf8') декодує неявно перший. Але так і робиться unicode(data), якщо дано strпредмет. :-) Використання io.open()дає вам більше можливостей, включаючи використання кодека, який записує BOM, і ви слідкуєте за даними JSON ще чимось іншим.
Martijn Pieters

@MartijnPieters: -базовий .encode('utf8')варіант працює як на Python 2, так і на 3 (той самий код). Немає unicodeна Python 3. Непов'язане: файли json не повинні використовувати BOM (хоча підтверджуючий аналізатор json може ігнорувати BOM, див. Помилку 3983 ).
jfs

додавання encoding='utf8'для json.dumpsвирішення проблеми. PS У мене є кириличний текст, щоб скинути
Max L

8

На Python 3.7 наступний код працює добре:

from json import dumps
result = {"symbol": "ƒ"}
json_string = dumps(result, sort_keys=True, indent=2, ensure_ascii=False)
print(json_string)

Вихід:

{"symbol": "ƒ"}

2
також у python 3.6 (тільки що перевірено).
Беррі Цакала

7

Далі - це моє розуміння відповіді на читання вару вище та google.

# coding:utf-8
r"""
@update: 2017-01-09 14:44:39
@explain: str, unicode, bytes in python2to3
    #python2 UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 7: ordinal not in range(128)
    #1.reload
    #importlib,sys
    #importlib.reload(sys)
    #sys.setdefaultencoding('utf-8') #python3 don't have this attribute.
    #not suggest even in python2 #see:http://stackoverflow.com/questions/3828723/why-should-we-not-use-sys-setdefaultencodingutf-8-in-a-py-script
    #2.overwrite /usr/lib/python2.7/sitecustomize.py or (sitecustomize.py and PYTHONPATH=".:$PYTHONPATH" python)
    #too complex
    #3.control by your own (best)
    #==> all string must be unicode like python3 (u'xx'|b'xx'.encode('utf-8')) (unicode 's disappeared in python3)
    #see: http://blog.ernest.me/post/python-setdefaultencoding-unicode-bytes

    #how to Saving utf-8 texts in json.dumps as UTF8, not as \u escape sequence
    #http://stackoverflow.com/questions/18337407/saving-utf-8-texts-in-json-dumps-as-utf8-not-as-u-escape-sequence
"""

from __future__ import print_function
import json

a = {"b": u"中文"}  # add u for python2 compatibility
print('%r' % a)
print('%r' % json.dumps(a))
print('%r' % (json.dumps(a).encode('utf8')))
a = {"b": u"中文"}
print('%r' % json.dumps(a, ensure_ascii=False))
print('%r' % (json.dumps(a, ensure_ascii=False).encode('utf8')))
# print(a.encode('utf8')) #AttributeError: 'dict' object has no attribute 'encode'
print('')

# python2:bytes=str; python3:bytes
b = a['b'].encode('utf-8')
print('%r' % b)
print('%r' % b.decode("utf-8"))
print('')

# python2:unicode; python3:str=unicode
c = b.decode('utf-8')
print('%r' % c)
print('%r' % c.encode('utf-8'))
"""
#python2
{'b': u'\u4e2d\u6587'}
'{"b": "\\u4e2d\\u6587"}'
'{"b": "\\u4e2d\\u6587"}'
u'{"b": "\u4e2d\u6587"}'
'{"b": "\xe4\xb8\xad\xe6\x96\x87"}'

'\xe4\xb8\xad\xe6\x96\x87'
u'\u4e2d\u6587'

u'\u4e2d\u6587'
'\xe4\xb8\xad\xe6\x96\x87'

#python3
{'b': '中文'}
'{"b": "\\u4e2d\\u6587"}'
b'{"b": "\\u4e2d\\u6587"}'
'{"b": "中文"}'
b'{"b": "\xe4\xb8\xad\xe6\x96\x87"}'

b'\xe4\xb8\xad\xe6\x96\x87'
'中文'

'中文'
b'\xe4\xb8\xad\xe6\x96\x87'
"""

5

Ось моє рішення за допомогою json.dump ():

def jsonWrite(p, pyobj, ensure_ascii=False, encoding=SYSTEM_ENCODING, **kwargs):
    with codecs.open(p, 'wb', 'utf_8') as fileobj:
        json.dump(pyobj, fileobj, ensure_ascii=ensure_ascii,encoding=encoding, **kwargs)

де для SYSTEM_ENCODING встановлено:

locale.setlocale(locale.LC_ALL, '')
SYSTEM_ENCODING = locale.getlocale()[1]

4

Використовуйте кодеки, якщо можливо,

with codecs.open('file_path', 'a+', 'utf-8') as fp:
    fp.write(json.dumps(res, ensure_ascii=False))

1

Дякую за оригінальну відповідь тут. З python 3 наступний рядок коду:

print(json.dumps(result_dict,ensure_ascii=False))

було добре. Спробуйте не писати занадто багато тексту в коді, якщо це не обов'язково.

Це може бути досить добре для консолі python. Однак, щоб задовольнити сервер, вам може знадобитися встановити локаль, як пояснено тут (якщо він знаходиться на apache2) http://blog.dscpl.com.au/2014/09/setting-lang-and-lcall-when-using .html

в основному встановити he_IL або будь-яку мову мови в ubuntu перевірити, чи він не встановлений

locale -a 

встановіть його там, де XX - ваша мова

sudo apt-get install language-pack-XX

Наприклад:

sudo apt-get install language-pack-he

додати наступний текст у / etc / apache2 / envvrs

export LANG='he_IL.UTF-8'
export LC_ALL='he_IL.UTF-8'

Тоді ви, сподіваємось, не отримаєте python-помилок від apache типу:

print (js) UnicodeEncodeError: 'ascii' кодек не може кодувати символи в позиції 41-45: порядковий не в діапазоні (128)

Також в апачі спробуйте зробити кодування за замовчуванням, як пояснено тут:
Як змінити кодування за замовчуванням на UTF-8 для Apache?

Робіть це рано, тому що помилки apache можуть бути налагодженими, і ви можете помилково подумати, що це з python, що, можливо, не так у цій ситуації


1

Якщо ви завантажуєте рядок JSON з файлу та вмісту арабських текстів. Тоді це спрацює.

Припустимо, як файл: arabic.json

{ 
"key1" : "لمستخدمين",
"key2" : "إضافة مستخدم"
}

Отримайте вміст арабської мови з файлу arabic.json

with open(arabic.json, encoding='utf-8') as f:
   # deserialises it
   json_data = json.load(f)
   f.close()


# json formatted string
json_data2 = json.dumps(json_data, ensure_ascii = False)

Щоб використовувати дані JSON у шаблоні Django, виконайте наведені нижче дії:

# If have to get the JSON index in Django Template file, then simply decode the encoded string.

json.JSONDecoder().decode(json_data2)

зроблено! Тепер ми можемо отримати результати у вигляді індексу JSON з арабським значенням.


fh.close() fhне визначено.
AMC

ІТ виправлено зараз. Було бf.close()
Чандан Шарма

0

використовуйте unicode-escape для вирішення проблеми

>>>import json
>>>json_string = json.dumps("ברי צקלה")
>>>json_string.encode('ascii').decode('unicode-escape')
'"ברי צקלה"'

поясніть

>>>s = '漢  χαν  хан'
>>>print('unicode: ' + s.encode('unicode-escape').decode('utf-8'))
unicode: \u6f22  \u03c7\u03b1\u03bd  \u0445\u0430\u043d

>>>u = s.encode('unicode-escape').decode('utf-8')
>>>print('original: ' + u.encode("utf-8").decode('unicode-escape'))
original:   χαν  хан

оригінальний ресурс: https://blog.csdn.net/chuatony/article/details/72628868


-3

Використання secure_ascii = False в json.dumps - це правильний напрямок для вирішення цієї проблеми, на що вказував Мартійн. Однак це може спричинити виняток:

UnicodeDecodeError: 'ascii' codec can't decode byte 0xe7 in position 1: ordinal not in range(128)

Вам потрібні додаткові налаштування в site.py або sitecustomize.py, щоб встановити ваш sys.getdefaultencoding () правильним. site.py знаходиться під lib / python2.7 /, а sitecustomize.py знаходиться під пакунками lib / python2.7 / site.

Якщо ви хочете використовувати site.py, у розділі def setencoding (): змініть перший, якщо 0: на 1: щоб python використовував локальну мову вашої операційної системи.

Якщо ви віддаєте перевагу використовувати sitecustomize.py, який може не існувати, якщо ви його не створили. просто поставте ці рядки:

import sys
reload(sys)
sys.setdefaultencoding('utf-8')

Тоді ви можете зробити деякий китайський вихід json у форматі utf-8, наприклад:

name = {"last_name": u"王"}
json.dumps(name, ensure_ascii=False)

Ви отримаєте закодовану рядок utf-8, а не \ u уникнуту рядок json.

Щоб перевірити кодування за замовчуванням:

print sys.getdefaultencoding()

Вам слід отримати "utf-8" або "UTF-8", щоб перевірити налаштування свого site.py або sitecustomize.py.

Зверніть увагу, що ви не можете виконати sys.setdefaultencoding ("utf-8") на інтерактивній консолі python.


2
ні. Не робіть цього. Змінення кодування символів за замовчуванням не має нічого спільного з json's ensure_ascii=False. Надайте мінімальний повний приклад коду, якщо ви думаєте про інше.
jfs

Цей виняток ви отримуєте лише в тому випадку, якщо або подаєте в байтові рядки, що не належать до ASCII (наприклад, не значення Unicode), або намагаєтеся поєднати отримане значення JSON (рядок Unicode) з рядом байтів, що не мають ASCII. Встановлення кодування за замовчуванням на UTF-8 по суті є маскуванням основної проблеми, якщо ви не керуєте належним чином своїми рядковими даними.
Мартійн Пітерс
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.