3

libxml2 の sax パーサーを使用して、大きな xml ファイルを読み取っています。ほとんどのコールバック ハンドラには、NULL で終了する char ポインタが提供されます。これらを使用String.fromCStringすると、Swift で通常の文字列に変換できます。ただし、sax はバイトの読み取りにバッファーを使用するため、コールバックの 1 つ ( characters) が文字列の一部、つまりバッファーのサイズで呼び出される場合があります。この部分的な文字列は、Unicode コード ポイントの途中で開始/終了することさえあります。完全な文字列が (チャンクで) 提供されるまで、コールバックは複数回呼び出されます。

完全な文字列が組み立てられるまですべてのチャンクを連結するか、部分的な文字列のコードポイント境界を何らかの方法で検出し、無効なコードポイントまで完全に処理することを考えています。

そのような状況を処理する最善の方法は何でしょうか? 処理は、正確でありながら、可能な限り高速にする必要があります。メモリ使用量は最小限に抑える必要がありますが、パフォーマンスを犠牲にしてはいけません。

4

2 に答える 2

2

endElement処理速度が最初の目標である場合は、XML 要素が完全に処理されて呼び出されるまで、すべての文字を収集します 。NSMutableData これは、Foundation フレームワークを使用して実行できます。したがって、プロパティが必要です

var charData : NSMutableData?

で初期化されstartElementます:

charData = NSMutableData()

コールバックでは、charactersすべてのデータを追加します。

charData!.appendBytes(ch, length: Int(len))

(強制アンラップはここで受け入れられます。が以前に呼び出されていない 場合にcharDataのみ可能です。これは、プログラミング エラーが発生したか、libxml2 が正しく動作していないことを意味します)。nilstartElement

最後にendElement、Swift 文字列を作成し、データを解放します。

defer {
    // Release data in any case before function returns
    charData = nil
}
guard let string =  String(data: charData!, encoding: NSUTF8StringEncoding) else {
    // Handle invalid UTF-8 data situation
} 
// string is the Swift string 
于 2015-12-19T20:47:37.377 に答える
1

最長の有効な UTF-8 文字は 4 バイトです ( RFC 3629セクション 3)。したがって、自分自身を安全に保つために非常に大きなバッファーは必要ありません。必要なバイト数のルールも非常に簡単です (最初のバイトを見てください)。したがって、0 ~ 3 バイトを保持するバッファを維持するだけです。適切な数値を取得したら、それを渡し、文字列を作成してみてください。このようなもの(軽くテストしただけで、まだ機能しないコーナーケースがあるかもしれません):

final class UTF8Parser {
    enum Error: ErrorType {
        case BadEncoding
    }
    var workingBytes: [UInt8] = []

    func updateWithBytes(bytes: [UInt8]) throws -> String {

        workingBytes += bytes

        var string = String()
        var index = 0

        while index < workingBytes.count {
            let firstByte = workingBytes[index]
            var numBytes = 0

                 if firstByte < 0x80 { numBytes = 1 }
            else if firstByte < 0xE0 { numBytes = 2 }
            else if firstByte < 0xF0 { numBytes = 3 }
            else                     { numBytes = 4 }

            if workingBytes.count - index < numBytes {
                break
            }

            let charBytes = workingBytes[index..<index+numBytes]

            guard let newString = String(bytes: charBytes, encoding: NSUTF8StringEncoding) else {
                throw(Error.BadEncoding)
            }
            string += newString
            index += numBytes
        }

        workingBytes.removeFirst(index)
        return string
    }
}

let parser = UTF8Parser()
var string = ""
string += try parser.updateWithBytes([UInt8(65)])

print(string)
let partial = try parser.updateWithBytes([UInt8(0xCC)])
print(partial)

let rest = try parser.updateWithBytes([UInt8(0x81)])
print(rest)

string += rest
print(string)

これは簡単な方法の 1 つにすぎません。おそらくより高速な別のアプローチは、コード ポイントの最後の開始点 (「10」で始まらないバイト) を探して、バイトを逆方向にたどることです。次に、その時点までのすべてを一挙に処理し、最後の数バイトだけを特別なケースにすることができます。

于 2015-12-19T17:34:26.930 に答える