Альтернативный парсер XML для ElementTree для облегчения проблем UTF-8? - PullRequest
9 голосов
/ 16 июля 2009

Я анализирую XML с помощью функции elementtree.parse (). Это работает, за исключением некоторых символов utf-8 (однобайтовый символ выше 128). Я вижу, что парсером по умолчанию является XMLTreeBuilder, основанный на экспате.

Есть ли альтернативный парсер, который я могу использовать, который может быть менее строгим и содержать символы utf-8?

Это ошибка, которую я получаю с парсером по умолчанию:

ExpatError: not well-formed (invalid token): line 311, column 190

Символ, вызывающий это, представляет собой один байт x92 (в шестнадцатеричном формате). Я не уверен, что это даже действительный символ utf-8. Но было бы неплохо с этим справиться, потому что большинство текстовых редакторов отображают это как: '

РЕДАКТИРОВАТЬ : Контекст персонажа: не могу, где я предполагаю, что это должен быть необычный апостраф, но в шестнадцатеричном редакторе та же последовательность: 63 61 6E 92 74

Ответы [ 4 ]

15 голосов
/ 17 июля 2009

Я начну с вопроса: «Есть ли альтернативный синтаксический анализатор, который я могу использовать, который может быть менее строгим и содержать символы utf-8?»

Все анализаторы XML будут принимать данные, закодированные в UTF-8. Фактически UTF-8 является кодировкой по умолчанию.

XML-документ может начинаться с объявления, подобного этому:

`<?xml version="1.0" encoding="UTF-8"?>`

или как это: <?xml version="1.0"?> или вообще не иметь декларации ... в каждом случае парсер будет декодировать документ с использованием UTF-8.

Однако ваши данные НЕ кодируются в UTF-8 ... это, вероятно, Windows-1252 aka cp1252.

Если кодировка не UTF-8, то либо создатель должен включить объявление (или получатель может добавить его заранее), либо получатель может перекодировать данные в UTF-8. Ниже показано, что работает, а что нет:

>>> import xml.etree.ElementTree as ET
>>> from StringIO import StringIO as sio

>>> raw_text = '<root>can\x92t</root>' # text encoded in cp1252, no XML declaration

>>> t = ET.parse(sio(raw_text))
[tracebacks omitted]
xml.parsers.expat.ExpatError: not well-formed (invalid token): line 1, column 9
# parser is expecting UTF-8

>>> t = ET.parse(sio('<?xml version="1.0" encoding="UTF-8"?>' + raw_text))
xml.parsers.expat.ExpatError: not well-formed (invalid token): line 1, column 47
# parser is expecting UTF-8 again

>>> t = ET.parse(sio('<?xml version="1.0" encoding="cp1252"?>' + raw_text))
>>> t.getroot().text
u'can\u2019t'
# parser was told to expect cp1252; it works

>>> import unicodedata
>>> unicodedata.name(u'\u2019')
'RIGHT SINGLE QUOTATION MARK'
# not quite an apostrophe, but better than an exception

>>> fixed_text = raw_text.decode('cp1252').encode('utf8')
# alternative: we transcode the data to UTF-8

>>> t = ET.parse(sio(fixed_text))
>>> t.getroot().text
u'can\u2019t'
# UTF-8 is the default; no declaration needed
4 голосов
/ 16 июля 2009

Похоже, у вас есть текст CP1252. Если это так, это должно быть указано вверху файла, например ::

<?xml version="1.0" encoding="CP1252" ?>

Это работает с ElementTree.

Если вы создаете эти файлы самостоятельно, не пишите их в этой кодировке. Сохраните их как UTF-8 и внесите свой вклад в уничтожение устаревших кодировок текста.

Если вы получаете данные CP1252 без спецификации кодировки и точно знаете, что это всегда будет CP1252, вы можете просто преобразовать их в UTF-8 перед отправкой в ​​анализатор:

s.decode("CP1252").encode("UTF-8")
1 голос
/ 16 июля 2009

Ах. Это «не может», очевидно, и действительно, 0x92 является апострофом во многих кодовых страницах Windows. Вместо этого ваш редактор предполагает, что это файл Mac. ;)

Если это одноразовый файл, исправление файла является правильным решением. Но почти всегда, когда вам нужно импортировать XML других людей, есть много вещей, которые просто не согласуются с заявленной кодировкой. Я обнаружил, что лучшее решение - это декодировать с установкой ошибки «xmlcharrefreplace», а в серьезных случаях делать собственную замену персонажа, которая устраняет наиболее распространенные проблемы для этого конкретного клиента.

Я также рекомендую lxml как библиотеку XML в Python, но здесь проблема не в этом.

1 голос
/ 16 июля 2009

Байт 0x92 никогда не будет действительным как первый байт символа UTF-8. Однако он может быть действительным в качестве последующего байта. См. это руководство по UTF-8 для таблицы допустимых последовательностей байтов.

Не могли бы вы дать нам представление о том, какие байты окружают 0x92? Содержит ли декларация XML кодировку символов?

...