python - xml.sax 解析器和行号等

标签 python xml-parsing saxparser

任务是解析一个简单的XML文档,并按行号分析内容。

正确的 Python 包似乎是 xml.sax。但是我该如何使用它呢?

在深入研究文档后,我发现:

  • xmlreader.Locator 接口(interface)有信息:getLineNumber()
  • handler.ContentHandler 接口(interface)有 setDocumentHandler()

第一个想法是创建一个 Locator,将其传递给 ContentHandler,并在调用其 character() 期间从 Locator 读取信息 方法等

但是,xmlreader.Locator 只是一个骨架接口(interface),并且只能从它的任何方法返回 -1。 因此,作为一个糟糕的用户,除了自己编写整个 ParserLocator 之外,我该怎么办??

我现在会回答我自己的问题。


(好吧,我会的,除非那个武断的、烦人的规则说我不能。)


我无法使用现有文档(或通过网络搜索)解决这个问题,因此不得不阅读 xml.sax 的源代码(在/usr/lib/python2.7 下/xml/sax/在我的系统上)。

默认情况下,xml.sax 函数 make_parser() 会创建一个真正的 Parser,但那是什么东西?
在源代码中,我们发现它是一个 ExpatParser,定义在 expatreader.py 中。 而且...它有自己的 Locator,一个 ExpatLocator。但是,没有访问这个东西。 这与解决方案之间出现了很多令人头疼的问题。

  1. 编写您自己的 ContentHandler,它知道 Locator,并使用它来确定行号
  2. 使用 xml.sax.make_parser() 创建一个 ExpatParser
  3. 创建一个 ExpatLocator,将其传递给 ExpatParser 实例。
  4. 制作ContentHandler,给它这个ExpatLocator
  5. ContentHandler 传递给解析器的 setContentHandler()
  6. 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/

相关文章:

python - 在python中的数据列之间添加文本

python - 如何验证xml编码

c++ - 使用 RapidXml 解析日文汉字时出错

java - SAXParserFactory 安全处理功能在 jboss 服务器中不起作用

android - 在 Android API 1.5 上使用 SAX 解析器 (javax.xml.parsers.SAXParser) 解析引号时出现问题

python - Pandas 过滤/数据缩减(1)有更好的方法吗?2)我做错了什么)

python - 在数据透视表中 - 如何在列中添加维度(除了维度行之外)?

python - 如何从另一个命名元组推导或子类型命名元组?

java - 为什么我的属性值没有打印?

java - 如何使 SAXParser 忽略转义码