4

タスクは、単純な XML ドキュメントを解析し、内容を行番号で分析することです。

正しい Python パッケージはxml.sax. しかし、どのように使用するのですか?

ドキュメントを掘り下げた後、次のことがわかりました。

  • xmlreader.Locatorインターフェイスには次の情報があります: getLineNumber()
  • handler.ContentHandlerインターフェイスにはsetDocumentHandler().

最初に考えられるのは、 を作成しLocator、これを に渡し、メソッドのContentHandler呼び出し中に Locator から情報を読み取る、などです。character()

ただし、xmlreader.Locatorはスケルトン インターフェイスにすぎず、そのメソッドのいずれからも -1 しか返せません。それで、貧弱なユーザーとして、私は何をすべきでしょうParserLocator?

私は今、自分の質問に答えます。


(まあ、私はできないという恣意的で迷惑なルールを除いて、私は持っているでしょう。)


既存のドキュメントを使用して (または Web 検索で) これを理解することができず、xml.sax(私のシステムの /usr/lib/python2.7/xml/sax/ の下にある) のソース コードを読むことを余儀なくされました。

xml.sax関数make_parser()はデフォルトで実数を作成しますが、それParserはどのようなものですか?
ソース コードでは、ExpatParserexpatreader.py で定義されている であることがわかります。そして...それは独自のLocator、を持っていExpatLocatorます。しかし、これにはアクセスできません。これと解決策の間で多くの頭を悩ませました。

  1. rContentHandlerについて知っている独自の を作成し、それを使用して行番号を決定しますLocato
  2. ExpatParserで作成xml.sax.make_parser()
  3. を作成し、インスタンスExpatLocatorに渡します。ExpatParser
  4. を作り、ContentHandlerこれを与えるExpatLocator
  5. ContentHandlerをパーサーに渡すsetContentHandler()
  6. を呼び出し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(または作成方法を見つける必要はありません)。欠点: どこにも文書化されておらず、文書化されていないプライベート変数を使用するのは適切ではありません。

ありがとうマルティン!

4

2 に答える 2

4

saxパーサー自体が、コンテンツハンドラーにロケーターを提供することになっています。ロケーターは特定のメソッドを実装する必要がありますが、適切なメソッドがあれば、どのオブジェクトでもかまいません。xml.sax.xmlreader.Locatorクラスは、ロケーターが実装することが期待されるインターフェースです。パーサーがハンドラーにロケーターオブジェクトを提供した場合、ロケーターに存在する4つのメソッドを信頼できます。

パーサーはロケーターを設定することのみを推奨されており、そうする必要はありません。expatXMLパーサーはそれを提供します。

サブクラス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)
于 2013-03-18T13:16:40.363 に答える