XMLとUnicodeとUTF-8@Python
文字コードで大混乱したがすっきり解決。昨日の日記には「なにもしてないのに文字化け」とか書いていたがよく見てみるとやっぱり「なにか」してるんですね。まず自分を疑うことを忘れずに。
基本的にやっていることは
2.の時点でA.xmlを見てみると問題がないのだが、6.で統合したXMLファイルを読み込むと文字化けしてひどいことになっているのである。エンコード指定が間違っているのだろうと考え、はじめはunicode()やencode()をとにかくいろいろな組み合わせで試してみてうまくいくものを探してみた。
とりあえず、よい組み合わせが見つかってうまく動いたがそれではなにも解決していないのでその後また検討をしてみる。Unicode関連が問題なのはわかっているのでUnicodeに関連する文書をもう一度読み直す。
そこでわかったことは以下のとおり。
from xml.dom.minidom import parse # test.xmlファイルをDOMツリーに読み込み dom = parse('test.xml')
としたときにPython内部ではデータをすべてUnicode文字列(not utf-8)として持っている。そのためdomに対してノードを追加したり、テキストを追加したりする際は必ずUnicode文字列に変換してから追加しなければならない。すなわちテキストノードを追加するときは以下のようにしなければならない。
# textをUnicodeにエンコード text = unicode(text) # テキストノードの作成 textelem = dom.createTextNode(text) # 子ノードの追加 node.appendChild(textelem)
結局この部分に問題があって以下のようにしていたのが間違いだった。最終的にutf-8にして出力するんだから、日本語はutf-8にしておいた方が良いだろうという考えである。しかし、はじめの時点ではこれでうまくいったが統合するとぐだぐだだったのだ。
# textをutf-8にエンコード text = text.encode('utf-8') # テキストノードの作成 textelem = dom.createTextNode(text) # 子ノードの追加 node.appendChild(textelem)
また、ノードの追加やらをしたあとのDOMをXMLファイルに書き出す際はUnicode文字列からutf-8文字列に変換してやらなければならない。しかし、writexmlでは書き出す際にエンコードの指定ができないため、codecsモジュールを使ってどの文字コードとして書き出すかを指定してすることが必要である。dom.toxml()として文字列を出力したあとにそれをencode()してもいいが、それよりこっちの方が楽である。
import codecs from xml.dom.minidom import parse dom = parse('test.xml') #ノードの追加やら削除やら # fileを使って書き込む際は文字列をutf-8に変換して書き込むことを指定 file = codecs.open('result.xml','wb',encoding='utf-8') # ヘッダのencodingをutf-8に指定して書き込み dom.writexml(file,'','\t','\n',encoding='utf-8') file.close() dom.unlink()
※注意
dom.writexml()のencoding指定は文字コードの変換に使われるのではなく、単にXMLファイルのヘッダ部分のencodingに設定する文字列を指定するだけである
<?xml version="1.0" encoding="utf-8"?>