2012-07-26 6 views
6

Zazwyczaj do odczytywania znaków ze strumienia bajtów używasz StreamReadera. W tym przykładzie czytam rekordy rozdzielane przez '\ r' z nieskończonego strumienia.Jak odczytujesz znaki UTF-8 z nieskończonego strumienia bajtów - 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(); 
     } 
    } 
} 

Problemem jest to, że StreamReader ma mały bufor wewnętrzny, więc jeśli kod czekając na „koniec zapisu” ogranicznika („\ r” w tym przypadku), to musi czekać aż do wewnętrznego bufora StreamReader za jest spłukiwany (zwykle dlatego, że przybyło więcej bajtów).

Ta alternatywna implementacja działa dla jednobajtowych znaków UTF-8, ale nie powiedzie się dla znaków wielobajtowych.

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(); 
    } 
} 

Jak mogę zmodyfikować ten kod, aby działał ze znakami wielobajtowymi?

+0

nie powinien być modyfikowany tytuł powiedzieć wielo-bajtowy lub UTF-16 znaków zamiast UTF-8? Wydaje się wprowadzać w błąd. –

+1

@TimS. Znaki UTF-8 mogą być więcej niż jednym bajtem. – Iridium

+0

@TimS. co masz na myśli? Wielobajtowy znak UTF-8 nie automagicznie staje się znakiem UTF-16. [Wiki] (http://en.wikipedia.org/wiki/UTF-8#Description). – CodeCaster

Odpowiedz

9

Zamiast Encoding.UTF8.GetChars który jest zaprojektowany do przekształcania kompletne bufory się wystąpienie Decoder i wielokrotnie wywołać sposób jego człon GetChars będzie wykorzystanie wewnętrznego bufora Decoder jest obsługiwać częściowe sekwencje wielu bitowych z końca jednego zadzwonić do następnego.

+0

Dzięki Richard, który działa świetnie. Zobacz moją odpowiedź na moją implementację. –

5

Dzięki Richardowi, mam teraz działający nieskończony czytnik strumienia. Jak wyjaśnił, sztuczka polega na użyciu instancji Decoder i wywołaniu jej metody GetChars. Przetestowałem to z wielobajtowym tekstem w języku japońskim i działa dobrze.

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

Nie rozumiem, dlaczego nie używasz metody ReadLine czytnika strumienia. Jeśli jest ku temu dobry powód, to jednak wydaje mi się, że wielokrotne wywoływanie GetChars na dekoderze jest nieefektywne. Dlaczego nie skorzystać z faktu, że reprezentacja bajtowa "\ r" nie może być częścią wielobajtowej sekwencji? (Bajty w sekwencji multi-bajtowego musi być większa niż 127;. To znaczy, że mają najwyższy ustawiony bit)

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

Czekaj, czy naprawdę mówisz, nazywając 'GetChars' na dekoder nieefektywnym, podczas odczytywania bajt po bajcie, umieszczając go wewnątrz listy bajtów, a następnie budując tablicę bajtów z tej listy i wywołując' Encoding.GetString' ? Wygląda na to, że przegapiłeś duży problem z wydajnością dla małego :) ... och, widzę, że OP zrobił to samo. Nieważne. – Luaan

Powiązane problemy