任务是解析一个简单的XML文档,并通过行号分析内容。在
正确的Python包似乎是xml.sax
。但我该如何使用它呢?在
在对文档进行了一番挖掘之后,我发现:
xmlreader.Locator
接口包含以下信息:getLineNumber()
。在handler.ContentHandler
接口有setDocumentHandler()
。在第一个想法是创建一个Locator
,将其传递给ContentHandler
,并在调用其character()
方法时从定位器读取信息,等等
但是,xmlreader.Locator
只是一个框架接口,并且只能从它的任何方法返回-1。
作为一个可怜的用户,我该怎么办,除了自己写一个完整的Parser
和{
我马上回答我自己的问题。在
(好吧,我本来可以的,除非有个武断、恼人的规定说我不能。)
我无法使用现有文档(或通过web搜索)来解决这一问题,因此被迫阅读xml.sax
(在我的系统上的/usr/lib/python2.7/xml/sax/下)的源代码。在
默认情况下,xml.sax
函数make_parser()
创建一个实的Parser
,但这是什么样的东西呢?
在源代码中,我们发现它是一个ExpatParser
,在expatreader.py.
而且…它有自己的Locator
,一个ExpatLocator
。但是,这东西是没有办法的。
在这和一个解决方案之间出现了许多令人头疼的问题。在
ContentHandler
,它知道Locato
r,并使用它来确定行号xml.sax.make_parser()
创建一个ExpatParser
ExpatLocator
,将其传递给ExpatParser
实例。在ContentHandler
,给它这个ExpatLocator
ContentHandler
传递给解析器的setContentHandler()
Parser
上调用parse()
。在例如:
import sys
import xml.sax
class EltHandler( xml.sax.handler.ContentHandler ):
def __init__( self, locator ):
xml.sax.handler.ContentHandler.__init__( self )
self.loc = locator
self.setDocumentLocator( self.loc )
def startElement( self, name, attrs ): pass
def endElement( self, name ): pass
def characters( self, data ):
lineNo = self.loc.getLineNumber()
print >> sys.stdout, "LINE", lineNo, data
def spit_lines( filepath ):
try:
parser = xml.sax.make_parser()
locator = xml.sax.expatreader.ExpatLocator( parser )
handler = EltHandler( locator )
parser.setContentHandler( handler )
parser.parse( filepath )
except IOError as e:
print >> sys.stderr, e
if len( sys.argv ) > 1:
filepath = sys.argv[1]
spit_lines( filepath )
else:
print >> sys.stderr, "Try providing a path to an XML file."
Martijn Pieters在下面指出了另一种具有一些优势的方法。
如果正确调用了ContentHandler
的超类初始值设定项,
然后发现一个私密的、没有文档记录的成员._locator
是
集合,它应该包含一个正确的Locator
。在
优点:您不必创建自己的Locator
(或了解如何创建它)。
缺点:它没有文档记录,使用未记录的私有变量是草率的。在
谢谢马蒂金!在
sax解析器本身应该为内容处理程序提供定位器。定位器必须实现某些方法,但它可以是任何对象,只要它有正确的方法。^{} class 是定位器应该实现的接口;如果解析器向处理程序提供了一个locator对象,那么就可以指望定位器上存在这4个方法了。在
解析器只鼓励设置定位器,不需要这样做。expat-XML解析器确实提供了它。在
如果您子类^{} ,那么它将为您提供一个标准的
setDocumentHandler()
方法,并且在调用处理程序上的.startDocument()
时,您的内容处理程序实例将设置self._locator
:这是一个老问题,但我认为有一个比给出的答案更好的答案,所以我还是要再加一个答案。在
虽然在ContentHandler超类中可能确实存在一个名为_locator的未记录的私有数据成员,正如Martijn在上面的回答中所描述的,但是在我看来,使用这个数据成员访问位置信息并不是定位工具的预期用途。在
在我看来,史蒂夫·怀特提出了一些很好的问题,为什么这个成员没有被记录在案。我认为这些问题的答案是,它可能不是为了供公众使用。它似乎是ContentHandler超类的私有实现细节。由于它是一个没有文档记录的私有实现细节,它可能会随着SAX库的任何未来版本而消失,因此依赖它可能是危险的。在
从阅读ContentHandler类的文档,特别是ContentHandler.setDocumentLocator,设计人员希望用户改为重写ContentHandler.setDocumentLocator函数,以便当解析器调用它时,用户的contenthandler子类可以保存对传入的locator对象(由SAX解析器创建)的引用,并且可以在以后使用保存的对象来获取位置信息。例如:
有了这种方法,就没有必要依赖于未记录的字段。在
相关问题 更多 >
编程相关推荐