-1

XML 1.0 では無効な Unicode XML 形式のテキスト ファイル内の文字を検索する Python 3.2 スクリプトを作成しています。ファイル自体は XML 1.0 ではないため、1.1 以降でサポートされている文字を簡単に含めることができますが、それを使用するアプリケーションは XML 1.0 で有効な文字しか処理できないため、それらを見つける必要があります。

XML 1.0 は、\u0009、\u000A、\u000D、および \u0020 を除いて、\u0001-\u0020 の範囲の文字をサポートしていません。その上では、\u0021-\uD7FF と \u010000-\u10FFFF もサポートされている範囲ですが、それ以外はありません。私の Python コードでは、正規表現パターンを次のように定義しています。

re.compile("[^\u0009\u000A\u000D\u0020\u0021-\uD7FF\uE000-\uFFFD\u010000-\u10FFFF]")

ただし、以下のコードでは、サンプル ファイルに既知の悪い文字 (\u0007、「ベル」文字) が見つかりません。残念ながら、サンプル行 (独自のデータ) を提供することはできません。

問題は次の 2 つの場所のいずれかにあると思います。正規表現パターンが悪いか、ファイルを開いて行単位で読み取る方法、つまりエンコードの問題です。もちろん、私は間違っているかもしれません。

関連するコード スニペットを次に示します。

processChunkFile()3 つのパラメータを取ります:chunkfileファイル (この場合、元のファイルの 500,000 行の「チャンク」) への絶対パスで、不正な文字が含まれている場合と含まれていない場合があります。outputfile出力を書き込むオプションの既存のファイルへの絶対パスです。verboseより詳細なコマンド ライン出力を有効にするブール フラグです。コードの残りの部分は、( を使用してargparse) コマンドライン引数を取得し、単一の大きなファイルを小さなファイルに分割するだけです。(元のファイルは通常 4GB を超えるため、「チャンク」する必要があります。)

def processChunkFile(chunkfile, outputfile, verbose):
    """
    Processes a given chunk file, looking for XML 1.0 chars.
    Outputs any line containing such a character.
    """
    badlines = []

    if verbose:
        print("Processing file {0}".format(os.path.basename(chunkfile)))

    # open given chunk file and read it as a list of lines
    with open(chunkfile, 'r') as chunk:
        chunklines = chunk.readlines()

        # check to see if a line contains a bad character;
        # if so, add it to the badlines list
        for line in chunklines:
            if badCharacterCheck(line, verbose) == True:
                badlines.append(line)

    # output to file if required
    if outputfile is not None:
        with open(outputfile.encode(), 'a') as outfile:
            for badline in badlines:
                outfile.write(str(badline) + '\n')

    # return list of bad lines
    return badlines



def badCharacterCheck(line, verbose):
    """
    Use regular expressions to seek characters in a line
    which aren't supported in XML 1.0.
    """
    invalidCharacters = re.compile("[^\u0009\u000A\u000D\u0020\u0021-\uD7FF\uE000-\uFFFD\u010000-\u10FFFF]")
    matches = re.search(invalidCharacters, line)
    if matches:
        if verbose:
            print(line)
            print("FOUND: " + matches.groups())
        return True

    return False
4

2 に答える 2

1
\u010000

Python\uエスケープは 4 桁のみであるため、U+0100 の後に 2 つの U+0030 数字のゼロが続きます。BMP 以外の文字には、8 桁の大文字 U エスケープを使用します。

\U00010000-\U0010FFFF

これと一般的な式は、文字列が UTF-16 コード単位に基づいており、BMP 外の文字が 2 つのサロゲート コード単位として処理される Python の「狭いビルド」では機能しないことに注意してください。(ナロー ビルドは Windows のデフォルトでした。ありがたいことに、Python 3.3 で廃止されました。)

1.1以降でサポートされている文字を簡単に含めることができます

(ただし、XML 1.1 はこれらの文字を数値文字参照としてエンコードされている場合にのみ含むことができる&#...;ため、ファイル自体は整形式ではない可能性があります。)

open(チャンクファイル, 'r')

チャンクファイルが でエンコードされていlocale.getpreferredencodingますか?

通常、元のファイルは 4GB を超えるため、「チャンク」する必要があります。

うーん、モンスターXMLは痛いです。しかし、合理的なストリーミング API (およびファイルシステム!) を使用すれば、それでも処理できるはずです。たとえば、 を使用してfor line in chunk:すべてのチャンクを一度に読み取る代わりに、を使用して各行を一度に 1 つずつ処理できますreadlines()

re.search(無効な文字、行)

invalidCharactersすでにコンパイル済みのパターン オブジェクトであるため、invalidCharacters.search(...).

そうは言っても、それでも私にとってはU + 0007ベルに一致します。

于 2013-10-16T00:15:35.773 に答える