3

我正在使用 libxml2 的 sax 解析器来读取大型 xml 文件。大多数回调处理程序都提供了一个以 NULL 结尾的字符指针。使用String.fromCString这些可以在 Swift 中转换为常规字符串。但是 sax 使用缓冲区来读取字节,因此characters可以使用字符串的一部分(即缓冲区的大小)调用回调 ( ) 之一。这个部分字符串甚至可能在 Unicode 代码点的中间开始/结束。回调将被多次调用,直到提供完整的字符串(以块的形式)。

我正在考虑连接所有块直到可以组装完整的字符串,或者以某种方式检测部分字符串中的代码点边界,仅处理完成直到无效代码点。

处理这种情况的最佳方法是什么?处理应该尽可能快,同时仍然正确。内存使用量应保持在最低限度,但不能以性能为代价。

4

2 回答 2

2

如果处理速度是您的首要目标,那么我将收集所有字符,直到 XML 元素被完全处理并被 endElement调用。这可以使用NSMutableData Foundation 框架来完成。所以你需要一个财产

var charData : NSMutableData?

初始化为startElement

charData = NSMutableData()

characters回调中附加所有数据:

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

(强制解包在这里是可以接受的。charData只能在nil 之前startElement没有被调用过,这意味着你犯了一个编程错误或 libxml2 不能正常工作)。

最后在 中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)

这只是一种直截了当的方式。另一种可能更快的方法是向后遍历字节,寻找代码点的最后一个开始(一个不以“10”开头的字节)。然后,您可以一口气处理到该点的所有内容,特殊情况下仅处理最后几个字节。

于 2015-12-19T17:34:26.930 回答