任务是解析一个简单的 XML 文档,并按行号分析内容。
正确的 Python 包似乎是xml.sax
. 但是我该如何使用它呢?
在对文档进行了一些挖掘之后,我发现:
xmlreader.Locator
界面有信息:getLineNumber()
。handler.ContentHandler
界面setDocumentHandler()
有。
第一个想法是创建一个Locator
,将其传递给ContentHandler
,并在调用其character()
方法等期间从 Locator 读取信息。
但是,xmlreader.Locator
只是一个骨架接口,只能从它的任何方法返回-1。所以作为一个可怜的用户,我该怎么办,没有写一个完整的Parser
和Locator
我自己的?
我现在会回答我自己的问题。
(嗯,我会的,除了说我不能的任意、烦人的规则。)
我无法使用现有文档(或通过网络搜索)弄清楚这一点,并被迫阅读xml.sax
(在我的系统上的 /usr/lib/python2.7/xml/sax/ 下)的源代码。
该xml.sax
函数make_parser()
默认创建一个 real Parser
,但那是什么东西?
在源代码中发现它是一个ExpatParser
,定义在 expatreader.py 中。并且...它有自己的Locator
, 一个ExpatLocator
. 但是,这个东西是无法访问的。在这和解决方案之间出现了很多令人头疼的问题。
- 编写自己
ContentHandler
的 r,它知道Locato
r,并用它来确定行号 - 创建
ExpatParser
一个xml.sax.make_parser()
- 创建一个
ExpatLocator
,将ExpatParser
实例传递给它。 - 做
ContentHandler
,给它这个ExpatLocator
- 传递
ContentHandler
给解析器setContentHandler()
- 呼吁。
parse()
_Parser
例如:
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
(或了解如何创建它)。缺点:它没有记录在案,并且使用未记录的私有变量是草率的。
谢谢马丁!