xml.parsers.expatのよくわからない挙動 - 解決
xml.parsers.expatのよくわからない挙動で取り上げた内容をPython issue Trackerへリポートしてみたら、xml.parsers.expatのバグではなく私のコードのバグであることをkawaiさんという方に指摘していただいた。結局「CharacterDataHandler」は渡されたデータを一回で読み込むとは限らない(SAXの仕様)ので、単にデータを渡すようにすると2回目以降に渡したデータで1回目に渡したデータを上書きてしまうのが原因らしい。(リンク先「The ContentHandler.characters() callback is missing data!」参照。)これを避けるために「CharacterDataHandler」に文字列を渡すときは「+=」で追加していく様にし、「StartElementHandler」で"link"要素の始めに来たときにクリアするようにする。
# -*- coding: utf-8 -*- from __future__ import with_statement from xml.parsers.expat import ParserCreate _cnt = "" _name = "" _data = "" _dict = {} def make(buf=False): """Make database which is accessible by Databese.dict""" global _cnt global _name global _data global _dict map_file = "/usr/share/icon-naming-utils/legacy-icon-mapping.xml" p = ParserCreate() p.buffer_text = buf p.StartElementHandler = start_element p.EndElementHandler = end_element p.CharacterDataHandler = char_data try: with open(map_file, 'rb') as f: p.ParseFile(f) except IOError, err: print err return _dict def start_element(name, attrs): global _cnt global _name global _data if name == 'context': _cnt = attrs["dir"] if name == 'icon': _name = attrs["name"] _dict[_name] = (_cnt, _name) if name == 'link': _data = '' # _dataをクリアする def end_element(name): global _cnt global _name global _data if name == 'link': _dict[_data] = (_cnt, _name) def char_data(data): global _data _data += data # _data に取得した文字列を追加する
kawaiさん有難うございました。
追記2009/01/27
"_data"のクリアをend_element()からstart_element()へ移動。さもないとスペースやタブ、改行が"_data"に入ってしまう。