2013-06-11 8 views
16

Quando si lavora con flussi binari (ovvero gli array byte[]), il punto principale dell'utilizzo di BinaryReader o BinaryWriter sembra essere la lettura/scrittura semplificata di tipi di dati primitivi da uno stream, utilizzando metodi come ReadBoolean() e prendendo in considerazione la codifica. È tutta la storia? C'è un vantaggio o svantaggio intrinseco se si lavora direttamente con uno Stream, senza utilizzare BinaryReader/BinaryWriter? La maggior parte dei metodi, come ad esempio Read(), sembra essere la stessa in entrambe le classi e la mia ipotesi è che funzionino in modo identico al di sotto.Utilizzo di Stream.Read() vs BinaryReader.Read() per elaborare flussi binari

consideri un semplice esempio di elaborazione di un file binario in due modi diversi (edit: mi rendo conto che in questo modo è inefficace e che un buffer può essere utilizzato, è solo un esempio):

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

l'uscita sarà essere lo stesso, ma cosa sta succedendo internamente (ad esempio dal punto di vista del sistema operativo)? In generale, è importante quale implementazione viene utilizzata? C'è qualche motivo per usare BinaryReader/BinaryWriter se non hai bisogno dei metodi extra che forniscono? Per questo caso specifico, MSDN dice questo per quanto riguarda Stream.ReadByte(): implementazione

L'impostazione predefinita in funzione crea un nuovo array singolo byte e quindi chiama Read. Mentre questo è formalmente corretto, è inefficiente.

Utilizzando GC.GetTotalMemory(), questo primo approccio sembra allocare 2x più spazio la seconda, ma AFAIK questo non dovrebbe essere il caso se un metodo più generale Stream.Read() viene utilizzato (ad esempio per la lettura in blocchi utilizzando un buffer). Tuttavia, mi sembra che questi metodi/interfacce possano essere facilmente unificati ...

+0

L'implementazione predefinita di Stream.ReadByte è destinata a essere superata in qualsiasi implementazione concreta di Stream. Il che lascia la domanda senza risposta, perché abbiamo bisogno di una nuova classe StreamReader invece di essere in grado di fare affidamento su (Streaming di) Stream per fare la cosa giusta? – yoyo

+0

@yoyo perché la classe Stream è mal progettata in generale. È troppo "generale". Non tutti i flussi supportano la ricerca o ReadByte (in modo efficiente) o la lettura o la scrittura. È solo un cattivo design OOP. Dovrebbero aver usato invece le interfacce. –

risposta

11

No, non c'è differenza principale tra i due approcci. Il Reader extra aggiunge un po 'di buffering, quindi non dovresti mischiarli. Ma non aspettatevi differenze significative nelle prestazioni, è tutto dominato dall'effettivo I/O.

Quindi,

  • usare un getto quando si hanno (solo) byte[] a muoversi. Come è comune in molti scenari di streaming.
  • utilizzare BinaryWriter e BinaryReader quando si dispone di un altro tipo di base (incluso il semplice byte) di dati da elaborare. Il loro scopo principale è la conversione dei tipi di framework integrati in byte[].
7

Una grande differenza è il modo in cui è possibile bufferizzare l'I/O. Se stai scrivendo/leggendo solo pochi byte qua o là, lo BinaryWriter/BinaryReader funzionerà bene. Ma se devi leggere MB di dati, leggerne uno byte, Int32, ecc. In un momento sarà un po 'lento. Potresti invece leggere pezzi più grandi e analizzare da lì.

Esempio:

// 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])); 
} 

Ora, questo è un po 'fuori tema ... ma mi butto là fuori: Se si voleva ottenere molto furbo ... e davvero per te è un incremento delle prestazioni ...(Anche se, potrebbe essere considerato pericoloso) Invece di parsing OGNI Int32, si potrebbe fare tutto in una volta usando Buffer.BlockCopy()

Un altro esempio:

// 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]); 
    } 
} 

Un paio di cose da notare in questo esempio: Questo si prende 4 byte per Int32 anziché uno ... Quindi produrrà risultati diversi. Puoi farlo anche per altri tipi di dati diversi da Int32, ma molti sostengono che il marshalling dovrebbe essere nella tua mente. (Volevo solo presentare qualcosa su cui riflettere ...)

+0

Grazie. L'esempio che ho fornito era ipotetico, mi rendo conto che non sarebbe efficace per i file di grandi dimensioni nella vita reale - in questo caso, faccio sempre l'approccio bufferizzato (il tuo primo esempio). Per come la capisco, sia 'Stream.Read()' che 'BinaryReader.Read()' ti permettono di fare la stessa cosa. Inoltre, dal momento che .NET 4, 'Stream.CopyTo()' fa questo per te. – w128

+1

Un'altra cosa a cui pensare è se stai facendo qualcosa come un tremare della mano TCP ... se stai scrivendo un byte alla volta, e il client legge in blocchi ... allora avrai un problema. Quindi in questo caso, ogni volta che scrivo, lo faccio a blocchi e, quando leggo, leggo con 'BinaryReader'. In questo modo, sono coperto. – Andrew

+0

Andrew: in tale scenario, credo che il comportamento sarebbe lo stesso usando sia 'Stream.Read()' che 'BinaryReader.Read()' se si usasse un buffer di un byte in entrambi i casi. – w128

Problemi correlati