任务是解析一个简单的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,并使用它来确定行号 - 使用
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
的父类(super class)初始值设定项,
然后结果是一个看起来很私密、没有记录的成员 ._locator
是
set,它应该包含一个合适的 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/