3

私は現在、XML ファイルを小さなチャンクに分割して、現在の基本的なファイルシステムと正規表現のアプローチの代わりに XMLReader と XMLWriter を使用しようとする PHP クラスを書き直しています。

ただし、XML プリアンブルからバージョン、エンコーディング、およびスタンドアロン フラグを取得する方法がわかりません。

テスト XML ファイルの先頭は次のようになります。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE fakedoctype SYSTEM "fake_doc_type.dtd">

 <!--
 This is a comment, it's here to try and get the parser to break in some way
 --> 

<root attribute="value" otherattribute="othervalue">

リーダーで問題なく開き、 read() 、 next() などを使用してドキュメントを移動できますが、 の内容を取得できないようです<?xml ... ?>。最初にアクセスできるのは、偽の DOCTYPE です。

私のテストコードは次のとおりです。

$a = new XMLReader ();
var_dump ($a -> open ('/path/to/test/file.xml')) // true
var_dump ($a -> nodeType); // 0
var_dump ($a -> name); // ""
var_dump ($a -> readOuterXML ()); // ''
var_dump ($a -> read ()); // true
var_dump ($a -> nodeType); // 10
var_dump ($a -> readOuterXML ()); // <!DOCTYPE fakedoctype SYSTEM "fake_doc_type.dtd">

もちろん、常に XML 1.0、エンコーディング UTF8、スタンドアロン = はいと仮定することもできますが、正確さのために、ソース フィードの値を取得し、分割ファイルを生成するときにそれらを使用できるようにしたいと考えています。

XMLReader と XMLwriter のドキュメントは非常に貧弱なようです。そのため、ドキュメントの何かを見落としている可能性があります。この場合どうすればよいかわかる方いますか?

4

1 に答える 1

3

定数があることから私が知っXMLReaderていることは 、 プロパティ内でXMLReader::XML_DECLARATIONドキュメントをトラバースするときに経験したことがありません。XMLReader::read()XMLReader::$nodeType

スキップされたように見えますが、これがなぜなのか疑問に思っていましたが、この動作を変更するためのフラグやオプションはまだ見つかりませんでした。

出力では、XMLReader常に UTF-8 でエンコードされた文字列を返します。これは、PHP の他の libxml ベースの部分と同じです。ですから、その側からは、すべてが明らかです。しかし、それはあなたが興味を持っている部分ではなく、で開いたファイルに入力された具体的な文字列だと思いますXMLReader::open()

XML宣言とBOMに基づいてXML文字列のエンコーディングを検出できる、私が名前を付けたユーティリティクラスをXMLReader作成したことがあります。両方やるべきだと思います。これはまだ正規表現を使用する必要がある部分だと思いますが、XML 宣言が最初に行われなければならず、非常によく厳密に定義された処理命令 (PI) であるため、そこをのぞくことができるはずです。XMLRecoder

これは、XMLRecoderコードの関連部分です。

### excerpt from https://gist.github.com/hakre/5194634 

/**
 * pcre pattern to access EncodingDecl, see <http://www.w3.org/TR/REC-xml/#sec-prolog-dtd>
 */
const DECL_PATTERN = '(^<\?xml\s+version\s*=\s*(["\'])(1\.\d+)\1\s+encoding\s*=\s*(["\'])(((?!\3).)*)\3)';
const DECL_ENC_GROUP = 4;
const ENC_PATTERN = '(^[A-Za-z][A-Za-z0-9._-]*$)';

...

($result = preg_match(self::DECL_PATTERN, $buffer, $matches, PREG_OFFSET_CAPTURE))
    && $result = $matches[self::DECL_ENC_GROUP];

このようにエンコードまで行っているので、完全ではありません。ただし、エンコーディングを抽出する必要がある場合 (および必要なバージョンの場合) は、それでうまくいくはずです。テスト用に、大量 (数千) のランダムな XML ドキュメントに対してこれを実行しました。

もう 1 つの部分は、BOM 検出です。

### excerpt from https://gist.github.com/hakre/5194634 

const BOM_UTF_8 = "\xEF\xBB\xBF";
const BOM_UTF_32LE = "\xFF\xFE\x00\x00";
const BOM_UTF_16LE = "\xFF\xFE";
const BOM_UTF_32BE = "\x00\x00\xFE\xFF";
const BOM_UTF_16BE = "\xFE\xFF";

...

/**
 * @param string $string string (recommended length 4 characters/octets)
 * @param string $default (optional) if none detected what to return
 * @return string Encoding, if it can not be detected defaults $default (NULL)
 * @throws InvalidArgumentException
 */
public function detectEncodingViaBom($string, $default = NULL)
{
    $len = strlen($string);

    if ($len > 4) {
        $string = substr($string, 0, 4);
    } elseif ($len < 4) {
        throw new InvalidArgumentException(sprintf("Need at least four characters, %d given.", $len));
    }

    switch (true) {
        case $string === self::BOM_UTF_16BE . $string[2] . $string[3]:
            return "UTF-16BE";

        case $string === self::BOM_UTF_8 . $string[3]:
            return "UTF-8";

        case $string === self::BOM_UTF_32LE:
            return "UTF-32LE";

        case $string === self::BOM_UTF_16LE . $string[2] . $string[3]:
            return "UTF-16LE";

        case $string === self::BOM_UTF_32BE:
            return "UTF-32BE";
    }

    return $default;
}

BOM 検出を使用して、同じ XML ドキュメントのセットに対してもこれを実行しましたが、BOM を含むものは多くありませんでした。ご覧のように、異なる BOM 間で重複するバイナリ パターンを処理しながら、より一般的なシナリオに合わせて検出順序が最適化されています。私が遭遇したほとんどのドキュメントは BOM なしであり、ドキュメントが UTF-32 でエンコードされているかどうかを確認するために主に必要です。

これが少なくともいくつかの洞察を与えることを願っています。

于 2013-03-19T09:14:47.287 に答える