Zapisywanie tekstów utf-8 w pliku json.dumps jako UTF8, a nie jako sekwencja ucieczki

472

przykładowy kod:

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

Problem: nie jest czytelny dla człowieka. Moi (inteligentni) użytkownicy chcą weryfikować, a nawet edytować pliki tekstowe za pomocą zrzutów JSON (i wolałbym nie używać XML).

Czy istnieje sposób szeregowania obiektów w ciągi JSON UTF-8 (zamiast \uXXXX)?

Berry Tsakala
źródło
9
+ for ברי צקלה :)))
rubmz

Odpowiedzi:

640

Użyj ensure_ascii=Falseprzełącznika do json.dumps(), a następnie ręcznie zakoduj wartość do 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())
"ברי צקלה"

Jeśli piszesz do pliku, po prostu użyj json.dump()i pozostaw obiektowi pliku do zakodowania:

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

Ostrzeżenia dotyczące Python 2

W przypadku Pythona 2 należy wziąć pod uwagę kilka dodatkowych zastrzeżeń. Jeśli piszesz to do pliku, możesz użyć io.open()zamiast open()do wytworzenia obiektu pliku, który koduje wartości Unicode podczas pisania, a następnie użyj json.dump()zamiast tego do zapisu do tego pliku:

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

Należy pamiętać, że istnieje błąd w jsonmodule , gdzie ensure_ascii=Falseflaga może wytworzyć mieszankę z unicodei strobiektów. Obejściem dla Python 2 jest zatem:

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))

W Pythonie 2, używając ciągów bajtów (typ str), zakodowanych w UTF-8, upewnij się również, że ustawiłeś encodingsłowo kluczowe:

>>> 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']
ברי צקלה
Martijn Pieters
źródło
72

Aby zapisać do pliku

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)

Aby wydrukować na standardowe wyjście

import json
print(json.dumps({"message":"xin chào việt nam"}, ensure_ascii=False))
Trần Quang Hiệp
źródło
1
Błąd składni: znak inny niż ASCII „\ xc3” w pliku json-utf8.py w wierszu 5, ale nie zadeklarowano kodowania; zobacz python.org/dev/peps/pep-0263 do szczegółów
Alex
Dziękuję Ci! Nie zdawałem sobie sprawy, że to takie proste. Musisz tylko uważać, jeśli dane, które konwertujesz na json, nie są niezaufane.
Karim Sonbol
@Alex, czy wymyśliłeś, jak uniknąć tego problemu?
Gabriel Fair
@ Gabriel szczerze mówiąc, nie pamiętam. Odłożenie fragmentu na bok nie było tak ważne :(
Alex
Działa tylko dla mnie przy użyciu codecsbiblioteki. Dzięki!
igorkf
29

AKTUALIZACJA: To zła odpowiedź, ale nadal warto zrozumieć, dlaczego jest zła. Zobacz komentarze.

Jak o unicode-escape?

>>> d = {1: "ברי צקלה", 2: u"ברי צקלה"}
>>> json_str = json.dumps(d).decode('unicode-escape').encode('utf8')
>>> print json_str
{"1": "ברי צקלה", "2": "ברי צקלה"}
monitorius
źródło
9
unicode-escapenie jest konieczne: możesz użyć json.dumps(d, ensure_ascii=False).encode('utf8')zamiast tego. I nie ma gwarancji, że json we wszystkich przypadkach używa dokładnie takich samych reguł jak unicode-escapekodek w Pythonie, tzn. Wynik może, ale nie musi być taki sam w niektórych przypadkach narożnych. Głosowanie dotyczy niepotrzebnej i prawdopodobnie złej konwersji. Niepowiązane: działa tylko dla ustawień regionalnych utf8 lub jeśli envvar określa tutaj utf8 (zamiast tego wypisuje Unicode). print json_strPYTHONIOENCODING
jfs
3
Kolejny problem: wszelkie cudzysłowy w wartościach łańcuchowych utracą swoje znaki ucieczki, co spowoduje przerwanie wyjścia JSON .
Martijn Pieters
błąd w Python3: AttributeError: obiekt „str” nie ma atrybutu „dekodowania”
Gank 18.04.16
1
unicode-escape działa dobrze! Zaakceptowałbym tę odpowiedź jako poprawną.
Pracownik
@ jfs Nie, json.dumps(d, ensure_ascii=False).encode('utf8')przynajmniej dla mnie nie działa. Dostaję - UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position ...błąd. Jednak unicode-escapewariant działa dobrze.
przetestowano
24

Obejście Petersa w python 2 kończy się niepowodzeniem w przypadku krawędzi:

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)

Wystąpił awaria w części .decode ('utf8') wiersza 3. Naprawiłem problem, czyniąc program o wiele prostszym, unikając tego kroku, a także specjalnej obudowy 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"}
Jonathan Ray
źródło
2
„Przypadek na krawędzi” był po prostu głupim, nieprzetestowanym błędem z mojej strony. Twoje unicode(data)podejście jest lepszą opcją niż używanie obsługi wyjątków. Zauważ, że encoding='utf8'argument słowa kluczowego nie ma nic wspólnego z json.dumps()produkowanym wyjściem ; służy do dekodowania strwejścia odbieranego przez funkcję.
Martijn Pieters
2
@MartijnPieters: lub prościej: open('filename', 'wb').write(json.dumps(d, ensure_ascii=False).encode('utf8'))Działa, czy dumpszwraca obiekt (tylko ascii) str lub obiekt Unicode.
jfs
@JFSebastian: racja, ponieważ najpierw str.encode('utf8') dekoduje domyślnie. Ale tak się stanie unicode(data), jeśli dany strprzedmiot. :-) Używanie io.open()daje jednak więcej opcji, w tym użycie kodeka, który zapisuje BOM i śledzisz dane JSON czymś innym.
Martijn Pieters
@MartijnPieters: .encode('utf8')wariant oparty na Pythonie 2 i 3 (ten sam kod). W unicodePythonie 3 nie ma żadnych danych . Niepowiązane: pliki json nie powinny używać BOM (chociaż parser potwierdzający json może zignorować BOM, patrz błąd 3983 ).
jfs
dodawanie w encoding='utf8'celu json.dumpsrozwiązania problemu. PS Mam tekst cyrylicy do zrzucenia
Max L
8

W Pythonie 3.7 następujący kod działa poprawnie:

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

Wynik:

{"symbol": "ƒ"}
Nik
źródło
2
także w python 3.6 (właśnie zweryfikowany).
Berry Tsakala
7

Oto moje zrozumienie var czytanie powyżej odpowiedzi i 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'
"""
Cheney
źródło
5

Oto moje rozwiązanie za pomocą 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)

gdzie SYSTEM_ENCODING jest ustawiony na:

locale.setlocale(locale.LC_ALL, '')
SYSTEM_ENCODING = locale.getlocale()[1]
Neit Sabes
źródło
4

Jeśli to możliwe, używaj kodeków,

with codecs.open('file_path', 'a+', 'utf-8') as fp:
    fp.write(json.dumps(res, ensure_ascii=False))
Yulin GUO
źródło
1

Dzięki za oryginalną odpowiedź tutaj. W Pythonie 3 następujący wiersz kodu:

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

było ok. Zastanów się, czy nie pisać zbyt dużo tekstu w kodzie, jeśli nie jest to konieczne.

Może to być wystarczające dla konsoli Pythona. Jednak, aby spełnić wymagania serwera, może być konieczne ustawienie ustawień regionalnych, jak wyjaśniono tutaj (jeśli jest na Apache2) http://blog.dscpl.com.au/2014/09/setting-lang-and-lcall-when-using .html

w zasadzie zainstaluj he_IL lub dowolny język w Ubuntu, sprawdź, czy nie jest zainstalowany

locale -a 

zainstaluj go tam, gdzie XX jest twoim językiem

sudo apt-get install language-pack-XX

Na przykład:

sudo apt-get install language-pack-he

dodaj następujący tekst do / etc / apache2 / envvrs

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

Miejmy nadzieję, że nie dostaniesz błędów Pythona z Apache, takich jak:

print (js) UnicodeEncodeError: kodek „ascii” nie może kodować znaków na pozycjach 41-45: porządek poza zakresem (128)

Również w Apache spróbuj ustawić utf jako domyślne kodowanie, jak wyjaśniono tutaj:
Jak zmienić domyślne kodowanie na UTF-8 dla Apache?

Zrób to wcześnie, ponieważ błędy apache mogą być problematyczne w debugowaniu i możesz błędnie pomyśleć, że pochodzi od Pythona, co prawdopodobnie nie ma miejsca w takiej sytuacji

sivi
źródło
1

Jeśli ładujesz ciąg JSON z pliku i jego zawartości, teksty arabskie. To zadziała.

Załóżmy, że plik taki jak: arabic.json

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

Pobierz arabską zawartość z pliku 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)

Aby użyć danych JSON w szablonie Django, wykonaj następujące kroki:

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

json.JSONDecoder().decode(json_data2)

gotowy! Teraz możemy uzyskać wyniki jako indeks JSON o wartości arabskiej.

Chandan Sharma
źródło
fh.close() fhjest niezdefiniowany.
AMC
To jest teraz poprawione. To byłobyf.close()
Chandan Sharma
0

użyj opcji unicode, aby rozwiązać problem

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

wyjaśnić

>>>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:   χαν  хан

oryginalny zasób :https://blog.csdn.net/chuatony/article/details/72628868

ChrisXiao
źródło
-3

Używanie sure_ascii = False w json.dumps jest właściwym kierunkiem do rozwiązania tego problemu, jak zauważył Martijn. Może to jednak powodować wyjątek:

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

Potrzebujesz dodatkowych ustawień w site.py lub sitecustomize.py, aby ustawić poprawność sys.getdefaultencoding (). site.py znajduje się w lib / python2.7 /, a sitecustomize.py jest w lib / python2.7 / site-packages.

Jeśli chcesz użyć site.py, w ramach def setencoding (): zmień pierwszy if 0: na if 1: aby python używał ustawień regionalnych twojego systemu operacyjnego.

Jeśli wolisz używać sitecustomize.py, który może nie istnieć, jeśli go nie utworzyłeś. po prostu wstaw te linie:

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

Następnie możesz wykonać jakieś chińskie wyjście json w formacie utf-8, takie jak:

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

Otrzymasz łańcuch zakodowany w utf-8, a nie łańcuch znaków json.

Aby zweryfikować domyślne kodowanie:

print sys.getdefaultencoding()

Powinieneś dostać „utf-8” lub „UTF-8”, aby zweryfikować ustawienia site.py lub sitecustomize.py.

Pamiętaj, że nie można wykonać sys.setdefaultencoding („utf-8”) w interaktywnej konsoli Pythona.

Ryan X
źródło
2
Nie. Nie rób tego Modyfikacja domyślnego kodowania znaków nie ma nic wspólnego z json„s” ensure_ascii=False. Podaj minimalny pełny przykład kodu, jeśli uważasz inaczej.
jfs
Ten wyjątek występuje tylko wtedy, gdy albo podajesz ciągi bajtów spoza ASCII (np. Nie wartości Unicode), albo próbujesz połączyć wynikową wartość JSON (ciąg Unicode) z ciągiem bajtów spoza ASCII. Ustawienie domyślnego kodowania na UTF-8 zasadniczo maskuje podstawowy problem, jeśli nie zarządzasz właściwie swoimi łańcuchami danych.
Martijn Pieters