2013-06-11 5 views
16

Bei der Arbeit mit binären Datenströmen (dh byte[] Arrays), scheint der Hauptpunkt der Verwendung von BinaryReader oder BinaryWriter sein vereinfachtes Lesen/Schreiben von primitiven Datentypen aus ein Stream, unter Verwendung von Methoden wie ReadBoolean() und unter Berücksichtigung der Codierung. Ist das die ganze Geschichte? Gibt es einen inhärenten Vorteil oder Nachteil, wenn man direkt mit einem Stream arbeitet, ohne BinaryReader/BinaryWriter zu verwenden? Die meisten Methoden, wie zum Beispiel Read(), scheinen in beiden Klassen gleich zu sein, und ich vermute, dass sie darunter identisch funktionieren.Stream.Read() vs BinaryReader.Read() zu verarbeiten Binärströme

Betrachten wir ein einfaches Beispiel eine binäre Datei auf zwei verschiedene Arten der Verarbeitung (edit: Ich weiß, das Art und Weise unwirksam ist und dass ein Puffer verwendet werden kann, ist es nur eine Probe):

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

Der Ausgang wird gleich sein, aber was passiert intern (z. B. aus Sicht des Betriebssystems)? Ist es im Allgemeinen wichtig, welche Implementierung verwendet wird? Gibt es einen Zweck, BinaryReader/BinaryWriter zu verwenden, wenn Sie die zusätzlichen Methoden nicht benötigen, die sie bereitstellen? Für diesen speziellen Fall, sagt MSDN dies in Bezug auf Stream.ReadByte():

Die Standardimplementierung auf Stream ist einen neuen Single-Byte-Array und ruft dann lesen. Während dies formal korrekt ist, ist es ineffizient.

Mit GC.GetTotalMemory() dieser erste Ansatz scheint 2x so viel Platz wie die zweite zuzuteilen, aber AFAIK sollte dies nicht der Fall sein, wenn ein allgemeiner Stream.Read() Methode verwendet wird (zB für in Stücke Lesen einer mit Puffer). Dennoch scheint es mir, dass diese Methoden/Schnittstellen einfach vereinheitlicht werden könnten ...

+0

Die Standardimplementierung von Stream.ReadByte soll in jeder konkreten Implementierung von Stream übergangen werden. Was lässt die Frage unbeantwortet, warum brauchen wir eine neue StreamReader-Klasse, anstatt uns auf (Implementierungen von) Stream zu verlassen, um das Richtige zu tun? – yoyo

+0

@yoyo, weil die Stream-Klasse im Allgemeinen schlecht entworfen ist. Es ist zu "allgemein". Nicht alle Streams unterstützen das Suchen oder ReadByte (effizient) oder Lesen oder Schreiben. Es ist einfach schlechtes OOP-Design. Sie sollten stattdessen Schnittstellen verwendet haben. –

Antwort

11

Nein, es gibt keinen prinzipiellen Unterschied zwischen den beiden Ansätzen. Der zusätzliche Reader fügt etwas Pufferung hinzu, so dass Sie sie nicht mischen sollten. Erwarten Sie jedoch keine wesentlichen Leistungsunterschiede, sondern alles wird von der tatsächlichen I/O dominiert.

So

  • einen Stream verwenden, wenn Sie (nur) byte[] zu bewegen. Wie es in vielen Streaming-Szenarien üblich ist.
  • Verwenden Sie BinaryWriter und BinaryReader, wenn Sie einen anderen Basistyp (einschließlich einfacher byte) der zu verarbeitenden Daten haben. Ihr Hauptzweck ist die Umwandlung der eingebauten Framework-Typen in byte[].
7

Ein großer Unterschied ist, wie Sie die E/A puffern können. Wenn Sie hier oder dort nur ein paar Bytes schreiben/lesen, wird BinaryWriter/BinaryReader gut funktionieren. Aber wenn Sie MBs von Daten lesen müssen, wird das Lesen eines byte, Int32 usw. auf einmal etwas langsam sein. Sie könnten stattdessen größere Stücke lesen und von dort analysieren.

Beispiel:

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

Nun ist dies ein bisschen off topic ... aber ich werde es dort werfen: Wenn Sie sehr schlau bekommen wollte ... und sich wirklich eine Leistungssteigerung geben ...(Wenn auch, könnte es gefährlich werden berücksichtigt) Anstatt jede Int32 Parsen, man konnte sie alle auf einmal tun mit Buffer.BlockCopy()

Ein weiteres Beispiel:

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

Ein paar Dinge über dieses Beispiel zu beachten: Dieser nimmt 4 Bytes pro Int32 anstelle von einem ... Also wird es andere Ergebnisse geben. Sie können dies auch für andere Datentypen als Int32 tun, aber viele würden argumentieren, dass Marshalling sollte in Ihrem Kopf sein. (Ich wollte nur etwas zum Nachdenken präsentieren ...)

+0

Danke. Das Beispiel, das ich zur Verfügung gestellt habe, war hypothetisch, ich weiß, dass es für große Dateien im wirklichen Leben nicht effektiv ist - ich mache immer den gepufferten Ansatz (Ihr erstes Beispiel) in diesem Fall. So wie ich es verstehe, können Sie mit "Stream.Read()" und "BinaryReader.Read()" dasselbe tun. Seit .NET 4 macht 'Stream.CopyTo()' das auch für Sie. – w128

+1

Eine andere Sache, über die Sie nachdenken sollten, ist, wenn Sie etwas wie ein TCP Handschütteln machen ... wenn Sie ein Byte nach dem anderen schreiben und der Client Blöcke einliest ... dann haben Sie ein Problem. Also, in diesem Fall, wenn ich schreibe, mache ich es in Blöcken, und wenn ich lese, lese ich mit dem 'BinaryReader'. Auf diese Weise bin ich abgedeckt. – Andrew

+0

Andrew: In einem solchen Szenario glaube ich, dass das Verhalten mit "Stream.Read()" und "BinaryReader.Read()" identisch wäre, wenn Sie in beiden Fällen einen Puffer von einem Byte verwenden würden. – w128

Verwandte Themen