任务是解析一个简单的 XML 文档,并按行号分析内容。
正确的 Python 包似乎是 xml.sax
。但是我该如何使用它呢?
在对文档进行了一些挖掘之后,我发现:
xmlreader.Locator
接口(interface)具有信息: getLineNumber()
。 handler.ContentHandler
接口(interface)有 setDocumentHandler()
。 第一个想法是创建一个
Locator
,将其传递给 ContentHandler
,并在调用其 character()
方法等期间从 Locator 读取信息。但是,
xmlreader.Locator
只是一个骨架接口(interface),并且只能从其任何方法返回 -1。所以作为一个可怜的用户,我该怎么办,没有自己写一个完整的
Parser
和 Locator
?我马上回答我自己的问题。
(好吧,我会的,除了说我不能的武断的、烦人的规则。)
我无法使用现有文档(或通过网络搜索)弄清楚这一点,并且被迫阅读
xml.sax
的源代码(在我的系统上的/usr/lib/python2.7/xml/sax/下)。xml.sax
函数 make_parser()
默认创建一个真正的 Parser
,但那是什么东西?在源代码中发现它是一个
ExpatParser
,定义在 expatreader.py 中。而且...它有自己的
Locator
,一个 ExpatLocator
。但是,没有办法接触到这个东西。在这个和解决方案之间出现了很多令人头疼的问题。
ContentHandler
,它知道 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
的父类(super class)初始化器被正确调用,然后结果是一个看起来很私密、没有证件的成员
._locator
是设置,它应该包含一个适当的
Locator
。优点:您不必创建自己的
Locator
(或了解如何创建它)。缺点:它没有任何记录,并且使用未记录的私有(private)变量是草率的。
谢谢马丁!
最佳答案
sax 解析器本身应该为您的内容处理程序提供定位器。定位器必须实现某些方法,但它可以是任何对象,只要它具有正确的方法。 xml.sax.xmlreader.Locator
class 是定位器需要实现的接口(interface);如果解析器为您的处理程序提供了一个定位器对象,那么您可以依靠定位器上存在的这 4 个方法。
仅鼓励解析器设置定位器,而不需要这样做。 expat XML 解析器确实提供了它。
如果您将 xml.sax.handler.ContentHandler()
子类化,那么它将为您提供一个标准的 setDocumentHandler()
方法,并且当处理程序上的 .startDocument()
被调用时,您的内容处理程序实例将设置 self._locator
:
from xml.sax.handler import ContentHandler
class MyContentHandler(ContentHandler):
def __init__(self):
ContentHandler.__init__(self)
# initialize your handler
def startElement(self, name, attrs):
loc = self._locator
if loc is not None:
line, col = loc.getLineNumber(), loc.getColumnNumber()
else:
line, col = 'unknown', 'unknown'
print 'start of {} element at line {}, column {}'.format(name, line, col)
关于python - xml.sax 解析器和行号等,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/15477363/