2012-07-26 82 views
6

通常,要從字節流中讀取字符,請使用StreamReader。在這個例子中,我正在讀取由無限流中'\ r'分隔的記錄。如何從無限字節流中讀取UTF-8字符 - C#

using(var reader = new StreamReader(stream, Encoding.UTF8)) 
{ 
    var messageBuilder = new StringBuilder(); 
    var nextChar = 'x'; 
    while (reader.Peek() >= 0) 
    { 
     nextChar = (char)reader.Read() 
     messageBuilder.Append(nextChar); 

     if (nextChar == '\r') 
     { 
      ProcessBuffer(messageBuilder.ToString()); 
      messageBuilder.Clear(); 
     } 
    } 
} 

的問題是,所述的StreamReader具有小的內部緩衝器,因此,如果等待(在這種情況下「\ R」)分隔符的「記錄結束」的碼它必須等待直到StreamReader的內部緩衝刷新(通常是因爲更多的字節已經到達)。

此替代實現適用於單字節UTF-8字符,但會在多字節字符上失敗。

int byteAsInt = 0; 
var messageBuilder = new StringBuilder(); 
while ((byteAsInt = stream.ReadByte()) != -1) 
{ 
    var nextChar = Encoding.UTF8.GetChars(new[]{(byte) byteAsInt}); 
    Console.Write(nextChar[0]); 
    messageBuilder.Append(nextChar); 

    if (nextChar[0] == '\r') 
    { 
     ProcessBuffer(messageBuilder.ToString()); 
     messageBuilder.Clear(); 
    } 
} 

我該如何修改此代碼以便它能夠處理多字節字符?

+0

不宜標題進行修改,說多字節或UTF-16字符,而不是UTF-8?似乎有誤導性。 – 2012-07-26 14:45:19

+1

@TimS。 UTF-8字符可以多於一個字節。 – Iridium 2012-07-26 14:46:37

+0

@TimS。你什麼意思?多字節UTF-8字符不會自動成爲UTF-16字符。 [維基](http://en.wikipedia.org/wiki/UTF-8#Description)。 – CodeCaster 2012-07-26 14:46:47

回答

9

不是Encoding.UTF8.GetChars其被設計成轉換完整緩衝劑,得到的Decoder一個實例,並反覆調用其成員方法GetChars這將利用Decoder的內部緩衝器的從一個的結束處理部分多字節序列打電話給下一個。

+0

謝謝理查德,這很好。看到我的答案我的實施。 – 2012-07-26 15:07:53

5

感謝理查德,我現在有一個工作無限流閱讀器。正如他解釋的,訣竅是使用解碼器實例並調用其GetChars方法。我用多字節的日文文本測試過,它工作正常。

int byteAsInt = 0; 
var messageBuilder = new StringBuilder(); 
var decoder = Encoding.UTF8.GetDecoder(); 
var nextChar = new char[1]; 

while ((byteAsInt = stream.ReadByte()) != -1) 
{ 
    var charCount = decoder.GetChars(new[] {(byte) byteAsInt}, 0, 1, nextChar, 0); 
    if(charCount == 0) continue; 

    Console.Write(nextChar[0]); 
    messageBuilder.Append(nextChar); 

    if (nextChar[0] == '\r') 
    { 
     ProcessBuffer(messageBuilder.ToString()); 
     messageBuilder.Clear(); 
    } 
} 
1

我不明白你爲什麼不使用流讀取器的ReadLine方法。但是,如果有一個不好的理由,但在我看來,在解碼器上反覆調用GetChars效率不高。爲什麼不利用'\ r'的字節表示不能成爲多字節序列的一部分的事實? (字節的多字節序列必須大於127;也就是說,它們擁有最高位設置)

var messageBuilder = new List<byte>(); 

int byteAsInt; 
while ((byteAsInt = stream.ReadByte()) != -1) 
{ 
    messageBuilder.Add((byte)byteAsInt); 

    if (byteAsInt == '\r') 
    { 
     var messageString = Encoding.UTF8.GetString(messageBuilder.ToArray()); 
     Console.Write(messageString); 
     ProcessBuffer(messageString); 
     messageBuilder.Clear(); 
    } 
} 
+0

等等,你是否認真地說在解碼器上調用'GetChars'效率低下,同時逐字節地讀取數據流,將它放在字節列表中,然後從該列表中構建一個字節數組並調用'Encoding.GetString' ?好像你已經錯過了那個小的性能問題:) ......哦,我看到OP做了同樣的事情。沒關係。 – Luaan 2014-01-14 08:17:27