web-dev-qa-db-fra.com

L'écriture de chaîne dans le flux et sa lecture ne fonctionne pas

Je veux écrire une chaîne dans un flux (un MemoryStream dans ce cas) et lire les octets un par un.

stringAsStream = new MemoryStream();
UnicodeEncoding uniEncoding = new UnicodeEncoding();
String message = "Message";

stringAsStream.Write(uniEncoding.GetBytes(message), 0, message.Length);

Console.WriteLine("This:\t\t" + (char)uniEncoding.GetBytes(message)[0]);
Console.WriteLine("Differs from:\t" + (char)stringAsStream.ReadByte());

Le résultat (indésirable) que j'obtiens est:

This:         M
Differs from: ?

Il semble comme s'il n'était pas lu correctement, car le premier caractère de "Message" est 'M', qui fonctionne lors de la récupération des octets de l'instance UnicodeEncoding mais pas lors de leur lecture dans le flux.

Qu'est-ce que je fais mal?


La vue d'ensemble: j'ai un algorithme qui fonctionnera sur les octets d'un Stream, je voudrais être aussi général que possible et travailler avec n'importe quel Stream. Je voudrais convertir une chaîne ASCII en un MemoryStream, ou peut-être utiliser une autre méthode pour pouvoir travailler sur la chaîne en tant que flux. L'algorithme en question fonctionnera sur les octets du Stream.

37
Deleted

Après avoir écrit dans le MemoryStream et avant de le relire, vous devez Seek revenir au début du MemoryStream pour ne pas lire depuis la fin.

MISE À JOUR

Après avoir vu votre mise à jour, je pense qu'il existe un moyen plus fiable de créer le flux:

UnicodeEncoding uniEncoding = new UnicodeEncoding();
String message = "Message";

// You might not want to use the outer using statement that I have
// I wasn't sure how long you would need the MemoryStream object    
using(MemoryStream ms = new MemoryStream())
{
    var sw = new StreamWriter(ms, uniEncoding);
    try
    {
        sw.Write(message);
        sw.Flush();//otherwise you are risking empty stream
        ms.Seek(0, SeekOrigin.Begin);

        // Test and work with the stream here. 
        // If you need to start back at the beginning, be sure to Seek again.
    }
    finally
    {
        sw.Dispose();
    }
}

Comme vous pouvez le voir, ce code utilise un StreamWriter pour écrire la chaîne entière (avec un encodage approprié) dans le MemoryStream. Cela vous évite de vous assurer que le tableau d'octets entier pour la chaîne est écrit.

Mise à jour: J'ai rencontré plusieurs fois un problème avec un flux vide. Il suffit d'appeler Flush juste après avoir fini d'écrire.

62
Justin Niessner

Essayez ce "one-liner" de Blog de Delta , String To MemoryStream (C #) .

MemoryStream stringInMemoryStream =
   new MemoryStream(ASCIIEncoding.Default.GetBytes("Your string here"));

La chaîne sera chargée dans le MemoryStream , et vous pourrez y lire. Voir Encoding.GetBytes (...) , qui a également été implémenté pour quelques autres encodages .

34
Joel Purra

Vous utilisez message.Length qui renvoie le nombre de caractères dans la chaîne, mais vous devez utiliser le nubmer de octets pour lire. Vous devez utiliser quelque chose comme:

byte[] messageBytes = uniEncoding.GetBytes(message);
stringAsStream.Write(messageBytes, 0, messageBytes.Length);

Vous lisez alors un seul octet et vous attendez à en obtenir un caractère simplement en le convertissant en char. UnicodeEncoding utilisera deux octets par caractère.

Comme Justin le dit, vous aussi ne cherchez pas à revenir au début du flux.

Fondamentalement, je crains à peu près que tout va mal ici. Veuillez nous donner une vue d'ensemble et nous pouvons vous aider à déterminer ce que vous devez vraiment faire. Utiliser un StreamWriter pour écrire puis un StreamReader pour lire est très probablement ce que vous voulez, mais nous ne pouvons pas vraiment le dire à partir du peu de code que vous avez montré.

14
Jon Skeet

Je pense qu'il serait beaucoup plus productif d'utiliser un TextWriter, dans ce cas un StreamWriter pour écrire dans le MemoryStream. Après cela, comme d'autres l'ont dit, vous devez "rembobiner" le MemoryStream en utilisant quelque chose comme stringAsStream.Position = 0L;.

stringAsStream = new MemoryStream();

// create stream writer with UTF-16 (Unicode) encoding to write to the memory stream
using(StreamWriter sWriter = new StreamWriter(stringAsStream, UnicodeEncoding.Unicode))
{
  sWriter.Write("Lorem ipsum.");
}
stringAsStream.Position = 0L; // rewind

Notez que:

StreamWriter utilise par défaut une instance de UTF8Encoding, sauf indication contraire. Cette instance de UTF8Encoding est construite sans une marque d'ordre d'octets (BOM)

De plus, vous n'avez pas besoin de créer une new UnicodeEncoding() en général, car il y en a déjà une en tant que membre statique de la classe à utiliser dans les saveurs pratiques utf-8, utf-16 et utf-32.

Et puis, enfin (comme d'autres l'ont dit), vous essayez de convertir les bytes directement en chars, ce qu'ils ne sont pas. Si j'avais un flux mémoire et savais que c'était une chaîne, j'utiliserais un TextReader pour récupérer la chaîne des octets. Il me semble "dangereux" de jouer avec les octets bruts.

5
Benny Jobigan

Vous devez réinitialiser le flux au début:

stringAsStream.Seek(0, SeekOrigin.Begin);
Console.WriteLine("Differs from:\t" + (char)stringAsStream.ReadByte());

Cela peut également être fait en définissant la propriété Position sur 0:

stringAsStream.Position = 0
1
Oded