任务是解析一个简单的 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 。但是,没有办法接触到这个东西。
    在这个和解决方案之间出现了很多令人头疼的问题。
  • 编写您自己的 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/

    10-12 21:01