python-如何解决xml.etree.ElementTree.iterparse()中的Unicode错误?
作者:互联网
我正在使用Python的xml.etree.ElementTree模块的iterparse()方法读取一个巨大的(千兆字节)XML文件.问题是某些XML文件的文本中偶尔会出现Unicode错误(或者至少是Python 3认为是Unicode错误).我的循环是这样设置的:
import xml.etree.ElementTree as etree
def foo():
# ...
f = open(filename, encoding='utf-8')
xmlit = iter(etree.iterparse(f, events=('start', 'end')))
(event, root) = next(xmlit)
for (event, elem) in xmlit: # line 26
if event != 'end':
continue
if elem.tag == 'foo':
do_something()
root.clear()
elif elem.tag == 'bar':
do_something_else()
root.clear()
# ...
当遇到带有Unicode错误的元素时,出现以下回溯错误:
Traceback (most recent call last):
File "<path to above file>", line 26, in foo
for (event, elem) in xmlit:
File "C:\Python32\lib\xml\etree\ElementTree.py", line 1314, in __next__
self._parser.feed(data)
File "C:\Python32\lib\xml\etree\ElementTree.py", line 1668, in feed
self._parser.Parse(data, 0)
UnicodeEncodeError: 'utf-8' codec can't encode character '\ud800' in position 16383: surrogates not allowed
由于错误发生在两次for循环迭代之间,因此我可以包装try块的唯一位置是在for循环之外,这意味着我无法继续下一个XML元素.
我优先考虑的解决方案如下:
>接收不必要的有效Unicode字符串作为元素的文本,而不是引发异常.
>接收有效的Unicode字符串,其中替换或删除了无效字符.
>跳过带有无效字符的元素,然后移至下一个.
如何在不亲自修改ElementTree代码的情况下实现这些解决方案中的任何一个?
解决方法:
首先,关于ElementTree的所有内容在这里可能都不相关.尝试仅枚举f = open(filename,encoding =’utf-8′)返回的文件,您可能会遇到相同的错误.
如果是这样,解决方案是覆盖默认的编码错误处理程序,如the docs中所述:
errors is an optional string that specifies how encoding and decoding errors are to be handled–this cannot be used in binary mode. Pass ‘strict’ to raise a ValueError exception if there is an encoding error (the default of None has the same effect), or pass ‘ignore’ to ignore errors. (Note that ignoring encoding errors can lead to data loss.) ‘replace’ causes a replacement marker (such as ‘?’) to be inserted where there is malformed data. When writing, ‘xmlcharrefreplace’ (replace with the appropriate XML character reference) or ‘backslashreplace’ (replace with backslashed escape sequences) can be used. Any other error handling name that has been registered with codecs.register_error() is also valid.
因此,您可以这样做:
f = open(filename, encoding='utf-8', errors='replace')
这符合您的第二优先级-无效字符将替换为’?’.
无法满足您的优先级,因为无法表示“不必要有效的Unicode字符串”.根据定义,Unicode字符串是Unicode代码点的序列,这就是Python处理str类型的方式.如果您有无效的UTF-8,并且想将其转换为字符串,则需要指定应如何将其转换为字符串,这就是错误的原因.
或者,您可以以二进制模式打开文件,并将UTF-8单独作为字节对象,而不是尝试将其转换为Unicode str对象,但是您只能使用与字节对象一起使用的API. (我相信ElementTree的lxml实现实际上可以做到这一点,但是内置的实现不能,但是请不要在上面引用我的意思.)但是即使您这样做了,也不会让您走得太远,因为XML代码本身将尝试解释无效的UTF-8,然后它需要知道您要对错误执行什么操作,而且通常很难指定,因为它的范围更广.
最后一点:
Since the error occurs in between for loop iterations, the only place I can wrap a try block is outside the for loop, which would mean I cannot continue to the next XML element.
好吧,实际上您不必使用for循环;您可以将其转换为带有显式下一个调用的while循环.每当您需要执行此操作时,通常都表明您做错了事情,但有时这表明您正在处理损坏的库,这是唯一可用的解决方法.
这个:
for (event, elem) in xmlit: # line 26
doStuffWith(event, elem)
有效等效于:
while True:
try:
event, elem = next(xmlit)
except StopIteration:
break
doStuffWith(event, elem)
现在,有一个明显的地方可以尝试一下-尽管您甚至不需要.您可以仅将另一个附加到现有的try上.
问题是,您打算在这里做什么?不能保证迭代器在引发异常后将能够继续.实际上,创建迭代器的所有最简单方法都将无法实现.您可以自己测试在这种情况下是否正确.
在极少数情况下,当您需要这样做并且确实有帮助时,您可能希望将其包装起来.像这样:
def skip_exceptions(it):
while True:
try:
yield next(it)
except StopIteration:
raise
except Exception as e:
logging.info('Skipping iteration because of exception {}'.format(e))
然后,您只需执行以下操作:
for (event, elem) in skip_exceptions(xmlit):
doStuffWith(event, elem)
标签:python-3-x,unicode,elementtree,xml,python 来源: https://codeday.me/bug/20191031/1975727.html