您可能要在此處使用的工具是UTF8
。它會爲你處理所有的州問題。請參閱How to cast decrypted UInt8 to String?瞭解一個可能適用的簡單示例。
從UTF-8數據構建字符串的主要問題不是組合字符,而是多字節字符。 「拉丁小寫字母A」+「組合GRAVE ACCENT」即使單獨解碼每個字符,也能正常工作。不工作的是收集你的第一個字節,解碼它,然後附加解碼的第二個字節。但是,UTF8
類型將爲您處理此問題。你所需要做的就是將你的NSInputStream
連接到GeneratorType
。
下面是我正在談論的一個基本的(並非完全生產就緒)示例。首先,我們需要一種將NSInputStream
轉換爲生成器的方法。這可能是最難的部分:
final class StreamGenerator {
static let bufferSize = 1024
let stream: NSInputStream
var buffer = [UInt8](count: StreamGenerator.bufferSize, repeatedValue: 0)
var buffGen = IndexingGenerator<ArraySlice<UInt8>>([])
init(stream: NSInputStream) {
self.stream = stream
stream.open()
}
}
extension StreamGenerator: GeneratorType {
func next() -> UInt8? {
// Check the stream status
switch stream.streamStatus {
case .NotOpen:
assertionFailure("Cannot read unopened stream")
return nil
case .Writing:
preconditionFailure("Impossible status")
case .AtEnd, .Closed, .Error:
return nil // FIXME: May want a closure to post errors
case .Opening, .Open, .Reading:
break
}
// First see if we can feed from our buffer
if let result = buffGen.next() {
return result
}
// Our buffer is empty. Block until there is at least one byte available
let count = stream.read(&buffer, maxLength: buffer.capacity)
if count <= 0 { // FIXME: Probably want a closure or something to handle error cases
stream.close()
return nil
}
buffGen = buffer.prefix(count).generate()
return buffGen.next()
}
}
呼叫至next()
可以阻止在這裏,所以它不應該在主隊列中被調用,但除此之外,它是吐出字節標準的生成。 (這也是可能有很多小角落案件,我不處理,所以你想仔細考慮這件事,但它並不複雜)。
因此,創建一個UTF- 8解碼生成幾乎是微不足道的:
final class UnicodeScalarGenerator<ByteGenerator: GeneratorType where ByteGenerator.Element == UInt8> {
var byteGenerator: ByteGenerator
var utf8 = UTF8()
init(byteGenerator: ByteGenerator) {
self.byteGenerator = byteGenerator
}
}
extension UnicodeScalarGenerator: GeneratorType {
func next() -> UnicodeScalar? {
switch utf8.decode(&byteGenerator) {
case .Result(let scalar): return scalar
case .EmptyInput: return nil
case .Error: return nil // FIXME: Probably want a closure or something to handle error cases
}
}
}
你當然可以平凡變成一個CharacterGenerator代替(使用Character(_:UnicodeScalar)
)。
最後一個問題是,如果要合併所有組合標記,例如「拉丁小寫字母A」和「組合標記ACCENT」之後總會一起返回(而不是它們的兩個字符)。這實際上比聽起來有點棘手。首先,你需要生成字符串,而不是字符。然後你需要一個很好的方法來知道所有的組合字符是什麼。這當然是可以知道的,但是我在導出一個簡單的算法時遇到了一些麻煩。 Cocoa中沒有「結合MarkCharacterSet」。我仍在考慮這個問題。獲得「主要工作」的東西很容易,但我不確定如何構建它,以便它適用於所有的Unicode。
這裏有一個小示例程序來嘗試一下:
let textPath = NSBundle.mainBundle().pathForResource("text.txt", ofType: nil)!
let inputStream = NSInputStream(fileAtPath: textPath)!
inputStream.open()
dispatch_async(dispatch_get_global_queue(0, 0)) {
let streamGen = StreamGenerator(stream: inputStream)
let unicodeGen = UnicodeScalarGenerator(byteGenerator: streamGen)
var string = ""
for c in GeneratorSequence(unicodeGen) {
print(c)
string += String(c)
}
print(string)
}
還有一點文如下:(即第二行是一些Zalgo encoded text,這是測試好的)
Here is some normalish álfa你好 text
And some Zalgo i̝̲̲̗̹̼n͕͓̘v͇̠͈͕̻̹̫͡o̷͚͍̙͖ke̛̘̜̘͓̖̱̬ composed stuff
And one more line with no newline
我還沒有在真正的阻塞情況下做過任何測試,比如從網絡上讀取,但它應該基於NSInputStream
如何工作(即它應該阻塞,直到至少有一個字節要讀取,但是應該只填充緩衝區中的可用內容)。
我做這一切的比賽GeneratorType
,以便它插入其他的東西很容易,但錯誤處理可能更好地工作,如果你沒有使用GeneratorType
,而是與next() throws -> Self.Element
,而不是創建自己的協議。投擲會讓堆疊中的錯誤更容易傳播,但會使插入for...in
循環變得更困難。
你想顯示部分字符串嗎?當收到所有數據時,連接將始終關閉/結束 – Wain
在這種情況下,我可能會立即從客戶端獲取多條消息。我需要將數據掃描爲字符串來分解它。 –
唯一的方法是按字節檢查您的流字節。你可以看到,如果下一個unicode字符由一個,兩個,三個,四個,五個或六個字節表示,並將字符串附加到整個字符。所以需要一些中間緩衝區。 – user3441734