Comment lire les caractères UTF-8 à partir d’un stream d’octets infini – C #

Normalement, pour lire les caractères d’un stream d’octets, utilisez un StreamReader. Dans cet exemple, je lis des enregistrements délimités par ‘\ r’ à partir d’un stream infini.

using(var reader = new StreamReader(stream, Encoding.UTF8)) { var messageBuilder = new SsortingngBuilder(); var nextChar = 'x'; while (reader.Peek() >= 0) { nextChar = (char)reader.Read() messageBuilder.Append(nextChar); if (nextChar == '\r') { ProcessBuffer(messageBuilder.ToSsortingng()); messageBuilder.Clear(); } } } 

Le problème est que StreamReader a un petit tampon interne. Par conséquent, si le code attend un délimiteur de “fin d’enregistrement” (“\ r” dans ce cas), il doit attendre que le tampon interne de StreamReader soit vidé (généralement parce que plusieurs octets sont nécessaires). est arrivé).

Cette implémentation alternative fonctionne pour les caractères UTF-8 à un octet, mais échouera pour les caractères multi-octets.

 int byteAsInt = 0; var messageBuilder = new SsortingngBuilder(); 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.ToSsortingng()); messageBuilder.Clear(); } } 

Comment puis-je modifier ce code afin qu’il fonctionne avec des caractères multi-octets?

Plutôt que Encoding.UTF8.GetChars qui est conçu pour convertir des tampons complets, obtenir une instance de Decoder et appeler à plusieurs resockets sa méthode membre GetChars tampon interne du Decoder sera utilisé pour gérer des séquences partielles à plusieurs octets à partir de la fin de celle-ci. appeler au suivant.

Grâce à Richard, j’ai maintenant un lecteur de stream de travail infini. Comme il l’a expliqué, l’astuce consiste à utiliser une instance de Decoder et à appeler sa méthode GetChars. Je l’ai testé avec du texte japonais multi-octets et cela fonctionne bien.

 int byteAsInt = 0; var messageBuilder = new SsortingngBuilder(); 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.ToSsortingng()); messageBuilder.Clear(); } } 

Je ne comprends pas pourquoi vous n’utilisez pas la méthode ReadLine du lecteur de stream. S’il y a une bonne raison de ne pas le faire, cependant, il me semble néanmoins qu’appeler à plusieurs resockets GetChars sur le décodeur est inefficace. Pourquoi ne pas utiliser le fait que la représentation octet de “\ r” ne peut pas faire partie d’une séquence multi-octets? (Les octets d’une séquence multi-octets doivent être supérieurs à 127, c’est-à-dire qu’ils ont le jeu de bits le plus élevé.)

 var messageBuilder = new List(); int byteAsInt; while ((byteAsInt = stream.ReadByte()) != -1) { messageBuilder.Add((byte)byteAsInt); if (byteAsInt == '\r') { var messageSsortingng = Encoding.UTF8.GetSsortingng(messageBuilder.ToArray()); Console.Write(messageSsortingng); ProcessBuffer(messageSsortingng); messageBuilder.Clear(); } } 

Mike, j’ai trouvé votre solution parfaite pour ma situation. Mais j’ai remarqué que parfois, il faut quatre appels GetChar () pour déterminer les caractères à renvoyer. Cela signifiait que charCount était à 2, alors que ma taille de tampon nextChar était de 1. J’ai donc eu l’erreur “Le tampon de caractères de sortie est trop petit pour contenir les caractères décodés, codant le repli Unicode System.Text.DecoderReplacementFallback.”

J’ai changé mon code en:

  // ... var nextChar = new char[4]; // 2 might suffice for (var i = startPos; i < bytesRead; i++) { int charCount; //... charCount = decoder.GetChars(buffer, i, 1, nextChar, 0); if (charCount == 0) { bytesSkipped++; continue; } for (int ic = 0; ic < charCount; ic++) { char c = nextChar[ic]; charPos++; // Process character here... } }