CodingFirst

C言語、Perl、JavaScript、最近はPythonも。出来上がったものより、プログラムを書くことが好き。あと、スイーツ。

スポンサーサイト

  • このエントリーをはてなブックマークに追加
  • web拍手 by FC2
上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

python2.6でdictionaryをjson化した際、日本語が\uXXXXにエスケープされないようにする

  • このエントリーをはてなブックマークに追加
  • web拍手 by FC2

Python2.6でdictionaryをjson文字列化したときに
日本語が\uXXXXとUTF-16な文字列にエスケープさせないようにするのに苦労した。
(忙しい人は、最後の要するに~の部分をご参考に)

Pythonでdictionaryをファイルに読み書きするとき、
テキストファイルで一番簡単なのはJSON文字列化する手だろう。
テキストにすると、エディタで編集できるし、diffもとりやすいし、gitやsvn管理した際に履歴も追いやすい。

だけど、JSON文字列化がむずかしい。
日本語が\\uXXXXとUTF-16な文字としてエスケープされてしまう。
これだと日本語部分をエディタで編集できなくなってしまうので、
いろいろ試してみた。

まず、dictionaryをJSONで書き出して、読み込んでみる。

$ python
Python 2.6.5 (r265:79063, Apr 16 2010, 13:09:56) 
[GCC 4.4.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import json
>>> json.dumps({'a':'hello'})
'{"a": "hello"}'

dictionaryをJSON文字列化するだけならすごく簡単。
だけど、日本語をまぜるとややこしくなる。

>>> json.dumps({'a':u'あああ'})
'{"a": "\\u3042\\u3042\\u3042"}'
>>> json.dumps({'a':'あああ'})
'{"a": "\\u3042\\u3042\\u3042"}'

バイト列でもユニコードでも結果は同じで、\uXXXXとUTF-16文字列化される(\が2個)。
公式サイトのドキュメントを見ると、どうやら ensure_ascii オプションの作用らしい。
19.2. json ― JSON エンコーダおよびデコーダ ― Python v2.6.2 documentation

デフォルトは、ensure_ascii=Trueなので、Falseにしてみる。

>>> json.dumps({'a':u'あああ'},ensure_ascii=False)
u'{"a": "\u3042\u3042\u3042"}'
>>> json.dumps({'a':'あああ'},ensure_ascii=False)
'{"a": "\xe3\x81\x82\xe3\x81\x82\xe3\x81\x82"}'

期待する挙動っぽいが、どう解釈するんだろ?
どちらの例もユニコード文字列化してない(\\になってない)のは期待どおり。
最初の例はJSON文字列全体をPythonユニコード文字列化してて、
次の例はJSON文字列をバイト列の文字列化している
...で、いいかな?

printしたら、ぐっとわかりやすくなるか?

>>> print json.dumps({'a':u'あああ'},ensure_ascii=False)
{"a": "あああ"}
>>> print json.dumps({'a':'あああ'},ensure_ascii=False)
{"a": "あああ"}

違いがない。余計、混乱した。

フィーリング的には違いが出そうなんだけどなぁ~。 端末がUTF-8だからかなー。

まー、いっか。
この結果は、printがなんか頑張ってたんだろうとして考えない事にする。

いよいよ、dictionaryをJSONに変換し、ファイルに書いてみる。

>>> import os
>>> f=open('out.json','w')
>>> json.dump({'a':u'あああ'},f)
>>> f.close()
>>> os.system('cat out.json')
{"a": "\u3042\u3042\u3042"}0

とりあえず、dictionaryをJSON化してファイルに書くのは簡単だし、
ファイルからJSONを読んで、dictionaryに戻すのも簡単だ。

>>> f=open('out.json','r')
>>> json.load(f)
{u'a': u'\u3042\u3042\u3042'}
>>> f.seek(0)
>>> s=json.load(f)
>>> print s['a']
あああ

便利。
あとは、書き出したファイルを日本語化するだけだ。
公式サイトでみた、ensure_ascii=False を使ってみよう。

>>> f=open('out.json','w')
>>> json.dump({'a':u'あああ'},f)
>>> f.close()
>>> json.dump({'a':u'あああ'},f,ensure_ascii=False)
Traceback (most recent call last):
  File "", line 1, in 
  File "/usr/lib/python2.6/json/__init__.py", line 181, in dump
    fp.write(chunk)
ValueError: I/O operation on closed file

エラーになった。
公式サイトの「問題になるだろう」発言はこの事だろう。
一応、バイト列でも試してみよう。

>>> f=open('out.json','w')
>>> json.dump({'a':'あああ'},f,ensure_ascii=False)
>>> f.close()
>>> print os.system('cat out.json')
{"a": "あああ"}0

これがうまくいってしまう...やだな。
なら、dictionary内の文字列をバイト列変換してJSON化すりゃいい
...とつい考えてしまう。
バイト列でその他の処理をするのは辛そうだし...とか言って。
しかし、この方向性は捨てる。あとあと面倒な事が増えるだけだ。

ここで、公式サイトのドキュメントを思い出す。
「fp.write() が (codecs.getwriter() のように) 前もって unicode に対応していると判っているもの」
とあったので、codecs を試してみる。
けど、とりあえずバイト列で。

>>> import codecs
>>> f=codecs.open('out.json','w','utf-8')
>>> json.dump({'a':'あああ'},f,ensure_ascii=False)
Traceback (most recent call last):
  File "", line 1, in 
  File "/usr/lib/python2.6/json/__init__.py", line 181, in dump
    fp.write(chunk)
  File "/usr/lib/python2.6/codecs.py", line 686, in write
    return self.writer.write(data)
  File "/usr/lib/python2.6/codecs.py", line 351, in write
    data, consumed = self.encode(object, self.errors)
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe3 in position 1: ordinal not in range(128)
>>> f.close()

バイト列だとエラーが出た。
バイト列内に非ASCII文字があるじゃないかと。これでいい。
では、ユニコードで試す。

>>> f=codecs.open('out.json','w','utf-8')
>>> json.dump({'a':u'あああ'},f,ensure_ascii=False)
>>> f.close()
>>> print os.system('cat out.json')
{"a": "あああ"}0

日本語で保存できた!
つまり、ユニコード文字列を含むdictionaryをエスケープせずにJSON化してファイルに書き出せた。

さて、読み込んでみる。読み込みにトラブルは無い。

>>> f=codecs.open('out.json','r','utf-8')
>>> json.load(f)
{u'a': u'\u3042\u3042\u3042'}
>>> f.seek(0)
>>> s=json.load(f)
>>> print s['a']
あああ

できたできた♪

要するに、次のように読み書きしろって事か。

>>> import json,codecs
>>> f=codecs.open('out.json','w','utf-8')
>>> json.dump({'a':u'あああ'},f,ensure_ascii=False)
>>> f=codecs.open('out.json','r','utf-8')
>>> s=json.load(f)
>>> print s['a']
あああ

これはPython2.6の例でした。
Python3.x だと文字列の取扱いが変わるらしいので、
もしかすると、もっと素直な挙動になるのかも知れない。
そうだといいな


みんなのPython 改訂版みんなのPython 改訂版
(2009/04/11)
柴田 淳

商品詳細を見る


Pythonチュートリアル 第2版Pythonチュートリアル 第2版
(2010/02/22)
Guido van Rossum

商品詳細を見る
スポンサーサイト


★☆★コメント★☆★

コメントの投稿

Name
Subject
Mail
URI
Comment
Pass
Secret 管理者にだけ表示を許可する

トラックバック

http://iyukki.blog56.fc2.com/tb.php/137-a7c6d848

 | HOME | 

Search

Recent Entries

Foot Print



Categories

Monthly

Recent Comments

Recent Trackbacks

Profile

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。