2016-11-17 2 views
0

Я ищу, чтобы прочитать следующий символ UTF8 из Stream или BinaryReader. Вещи, которые не работают:Чтение одного символа UTF8 из потока в C#

BinaryReader :: ReadChar - это набросит символ 3 или 4 байта. Поскольку он возвращает структуру двух байтов, у него нет выбора.

BinaryReader :: ReadChars - это будет выдаваться, если вы попросите его прочитать 1 символ, и он встретит символ 3 или 4 байта. Будет читать несколько символов, если вы попросите его прочитать более 1 символа.

StreamReader :: Read - это должно знать, сколько байтов читать, но количество байтов в символе UTF8 является переменной.

код я, что, кажется, работает:

private char[] ReadUTF8Char(Stream s) 
    { 
     byte[] bytes = new byte[4]; 
     var enc = new UTF8Encoding(false, true); 
     if (1 != s.Read(bytes, 0, 1)) 
      return null; 
     if (bytes[0] <= 0x7F) //Single byte character 
     { 
      return enc.GetChars(bytes, 0, 1); 
     } 
     else 
     { 
      var remainingBytes = 
       ((bytes[0] & 240) == 240) ? 3 : (
       ((bytes[0] & 224) == 224) ? 2 : (
       ((bytes[0] & 192) == 192) ? 1 : -1 
      )); 
      if (remainingBytes == -1) 
       return null; 
      s.Read(bytes, 1, remainingBytes); 
      return enc.GetChars(bytes, 0, remainingBytes + 1); 
     } 
    } 

Очевидно, это немного беспорядок, и несколько специфична в UTF8. Есть ли более элегантное, менее обычное, более легкое для чтения решение этой проблемы?

+0

Возможный дубликат http://stackoverflow.com/questions/11671826/how-do-you-read-utf-8-characters-from- an-infin-byte-stream-c-sharp –

+0

Вопрос может быть дубликатом, но этот ответ не работает. В частности, он не обрабатывает суррогатные пары. Я попытался изменить его, чтобы использовать 2-элементный буфер символов, но это только что вызвало другую проблему. Кроме суррогатных пар, все равно работает. – DDurschlag

+0

Прохладный, я не был на 100% уверен, что если бы это было то же самое, но я подумал, что в нем может быть полезная информация. –

ответ

0

Я знаю, что этот вопрос немного стар, но вот еще одно решение. Это не так хорошо работает, как решение OPs (которое я также предпочитаю), но оно использует только встроенные функции utf8, не зная о внутренних компонентах utf8.

private static char ReadUTF8Char(Stream s) 
{ 
    if (s.Position >= s.Length) 
     throw new Exception("Error: Read beyond EOF"); 

    using (BinaryReader reader = new BinaryReader(s, Encoding.Unicode, true)) 
    { 
     int numRead = Math.Min(4, (int)(s.Length - s.Position)); 
     byte[] bytes = reader.ReadBytes(numRead); 
     char[] chars = Encoding.UTF8.GetChars(bytes); 

     if (chars.Length == 0) 
      throw new Exception("Error: Invalid UTF8 char"); 

     int charLen = Encoding.UTF8.GetByteCount(new char[] { chars[0] }); 

     s.Position += (charLen - numRead); 

     return chars[0]; 
    } 
} 

Кодировка, переданная конструктору BinaryReader, не имеет значения. Мне пришлось использовать эту версию конструктора, чтобы оставить поток открытым. Если у вас уже есть двоичный считыватель вы можете просто использовать это:

private static char ReadUTF8Char(BinaryReader reader) 
{ 
    var s = reader.BaseStream; 

    if (s.Position >= s.Length) 
     throw new Exception("Error: Read beyond EOF"); 

    int numRead = Math.Min(4, (int)(s.Length - s.Position)); 
    byte[] bytes = reader.ReadBytes(numRead); 
    char[] chars = Encoding.UTF8.GetChars(bytes); 

    if (chars.Length == 0) 
     throw new Exception("Error: Invalid UTF8 char"); 

    int charLen = Encoding.UTF8.GetByteCount(new char[] { chars[0] }); 

    s.Position += (charLen - numRead); 

    return chars[0]; 
} 
Смежные вопросы