XMLとUnicodeとUTF-8@Python

文字コードで大混乱したがすっきり解決。昨日の日記には「なにもしてないのに文字化け」とか書いていたがよく見てみるとやっぱり「なにか」してるんですね。まず自分を疑うことを忘れずに。


基本的にやっていることは

  1. A.csvを読み込む
  2. 列名とタグ名の対応をとってA.xmlを作成
  3. A.xmlを読み込み
  4. 統合先のDB.xmlを読み込み
  5. 整合をとってA.xmlをDB.xmlに追加
  6. DB.xmlを書き出し


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"?>