web-dev-qa-db-fra.com

Utilisation de Stream.Read () vs BinaryReader.Read () pour traiter les flux binaires

Lorsque vous travaillez avec des flux binaires (c'est-à-dire des tableaux byte[]), Le point principal de l'utilisation de BinaryReader ou BinaryWriter semble être une lecture/écriture simplifiée des types de données primitifs d'un flux, en utilisant des méthodes telles que ReadBoolean() et en tenant compte de l'encodage. C'est toute l'histoire? Y a-t-il un avantage ou un inconvénient inhérent si l'on travaille directement avec un Stream, sans utiliser BinaryReader/BinaryWriter? La plupart des méthodes, telles que Read(), semblent être les mêmes dans les deux classes, et je suppose qu'elles fonctionnent de manière identique en dessous.

Prenons un exemple simple de traitement d'un fichier binaire de deux manières différentes (édition: je me rends compte que cette méthode est inefficace et qu'un tampon peut être utilisé, c'est juste un échantillon):

// Using FileStream directly
using (FileStream stream = new FileStream("file.dat", FileMode.Open))
{
    // Read bytes from stream and interpret them as ints
    int value = 0;
    while ((value = stream.ReadByte()) != -1)
    {
        Console.WriteLine(value);
    }
}


// Using BinaryReader
using (BinaryReader reader = new BinaryReader(FileStream fs = new FileStream("file.dat", FileMode.Open)))
{
    // Read bytes and interpret them as ints
    byte value = 0;    
    while (reader.BaseStream.Position < reader.BaseStream.Length)
    {
        value = reader.ReadByte();
        Console.WriteLine(Convert.ToInt32(value));
    }
}

La sortie sera la même, mais que se passe-t-il en interne (par exemple du point de vue du système d'exploitation)? Est-il - d'une manière générale - important de savoir quelle mise en œuvre est utilisée? Y a-t-il un intérêt à utiliser BinaryReader/BinaryWriter Si vous n'avez pas besoin des méthodes supplémentaires fournies? Pour ce cas spécifique, MSDN dit ceci en ce qui concerne Stream.ReadByte():

L'implémentation par défaut sur Stream crée un nouveau tableau à un octet, puis appelle Read. Bien que cela soit formellement correct, il est inefficace.

En utilisant GC.GetTotalMemory(), cette première approche semble allouer 2x autant d'espace que la seconde, mais AFAIK cela ne devrait pas être le cas si une méthode plus générale Stream.Read() est utilisée (par exemple pour la lecture en morceaux à l'aide d'un tampon). Pourtant, il me semble que ces méthodes/interfaces pourraient être unifiées facilement ...

19
w128

Non, il n'y a pas de différence principale entre les deux approches. Le lecteur supplémentaire ajoute un tampon, vous ne devez donc pas les mélanger. Mais ne vous attendez pas à des différences de performances significatives, tout est dominé par les E/S réelles.

Donc,

  • utilisez un flux lorsque vous avez (uniquement) byte[] bouger. Comme cela est courant dans de nombreux scénarios de streaming.
  • utilisez BinaryWriter et BinaryReader lorsque vous avez tout autre type de base (y compris un simple byte) de données à traiter. Leur objectif principal est la conversion des types de framework intégrés en byte[].
14
Henk Holterman

Une grande différence réside dans la façon dont vous pouvez mettre en tampon les E/S. Si vous écrivez/lisez seulement quelques octets ici ou là, BinaryWriter/BinaryReader Fonctionnera bien. Mais si vous devez lire des Mo de données, lire un byte, Int32, Etc ... à la fois sera un peu lent. Vous pouvez à la place lire de plus gros morceaux et analyser à partir de là.

Exemple:

// Using FileStream directly with a buffer
using (FileStream stream = new FileStream("file.dat", FileMode.Open))
{
    // Read bytes from stream and interpret them as ints
    byte[] buffer = new byte[1024];
    int count;
    // Read from the IO stream fewer times.
    while((count = stream.Read(buffer, 0, buffer.Length)) > 0)
        for(int i=0; i<count; i++)
           Console.WriteLine(Convert.ToInt32(buffer[i]));
}

Maintenant, c'est un peu hors sujet ... mais je vais le dire: si vous vouliez devenir TRÈS astucieux ... et vraiment vous donner un coup de pouce aux performances ... (Bien que cela puisse être considéré comme dangereux) Au lieu de en analysant CHAQUE Int32, vous pouvez tout faire en même temps en utilisant Buffer.BlockCopy()

Un autre exemple:

// Using FileStream directly with a buffer and BlockCopy
using (FileStream stream = new FileStream("file.dat", FileMode.Open))
{
    // Read bytes from stream and interpret them as ints
    byte[] buffer = new byte[1024];
    int[] intArray = new int[buffer.Length >> 2]; // Each int is 4 bytes
    int count;
    // Read from the IO stream fewer times.
    while((count = stream.Read(buffer, 0, buffer.Length)) > 0)
    {
       // Copy the bytes into the memory space of the Int32 array in one big swoop
       Buffer.BlockCopy(buffer, 0, intArray, count);

       for(int i=0; i<count; i+=4)
          Console.WriteLine(intArray[i]);
    }
}

Quelques choses à noter à propos de cet exemple: Celui-ci prend 4 octets par Int32 au lieu d'un ... Il donnera donc des résultats différents. Vous pouvez également le faire pour d'autres types de données autres que Int32, mais beaucoup diront que le marshaling devrait être dans votre esprit. (Je voulais juste présenter quelque chose à penser ...)

11
poy