2012-04-22 11 views
14

In jeder Java-Implementierung, die ich vom Lesen einer Datei sehe, sehe ich fast immer einen Dateileser, der Zeile für Zeile gelesen wird. Mein Gedanke wäre, dass dies furchtbar ineffizient wäre, weil es einen Systemaufruf pro Leitung erfordert.Java - Lesen von einer Datei. Eingabestream vs. Reader

Was ich stattdessen getan habe, ist einen Eingabestream zu verwenden und die Bytes direkt zu greifen. In meinen Experimenten ist dies deutlich schneller. Mein Test war eine 1 MB-Datei.

//Stream method 
    try { 
     Long startTime = new Date().getTime(); 

     InputStream is = new FileInputStream("test"); 
     byte[] b = new byte[is.available()]; 
     is.read(b); 
     String text = new String(b); 
     //System.out.println(text); 

     Long endTime = new Date().getTime(); 
     System.out.println("Text length: " + text.length() + ", Total time: " + (endTime - startTime)); 

    } 
    catch (Exception e) { 
     e.printStackTrace(); 
    } 

    //Reader method 
    try { 
     Long startTime = new Date().getTime(); 

     BufferedReader br = new BufferedReader(new FileReader("test")); 
     String line = null; 
     StringBuilder sb = new StringBuilder(); 
     while ((line = br.readLine()) != null) { 
      sb.append(line); 
      sb.append("\n"); 
     } 
     String text = sb.toString(); 

     Long endTime = new Date().getTime(); 
     System.out.println("Text length: " + text.length() + ", Total time: " + (endTime - startTime)); 

    } 
    catch (Exception e) { 
     e.printStackTrace(); 
    } 

Daraus ergibt sich ein Ergebnis von:

Text length: 1054631, Total time: 9 
Text length: 1034099, Total time: 22 

Also, warum Menschen nutzen Leser statt Streams?

Wenn ich eine Methode habe, die eine Textdatei nimmt und einen String zurückgibt, der den gesamten Text enthält, ist es dann unbedingt besser, einen Stream zu verwenden?

+0

Ihr Code ist nicht korrekt. Es ist nicht garantiert, dass es die gesamte Datei lesen wird, siehe die Dokumentation der gelesenen und verfügbaren Methoden. – Milo

+1

Hatten Sie versucht, [Files.readAllLines ([java.nio.File] (http://docs.oracle.com/javase/7/docs/api/java/nio/file/Files.html)) Paket (.. .) Methode. –

+0

+1 für etwas Neues gelernt – panny

Antwort

10

Sie vergleichen Äpfel mit Bananen. Das Lesen einer Zeile nach der anderen wird auch mit einem gepufferten Leser weniger effizient sein, als Daten so schnell wie möglich zu erfassen. Beachten Sie, dass die Verwendung verfügbar ist, da es nicht in allen Situationen korrekt ist. Ich fand das selbst heraus, als ich begann, Verschlüsselungsströme zu verwenden.

+0

Das ist sehr interessant. Ist beim Lesen aus einer Nur-Text-Datei, die auf dem lokalen Dateisystem vorhanden ist, gefährlich? – Jeremy

+0

@ Jeremy Es ist niemals richtig, ['available'] (http://docs.oracle.com/javase/7/docs/api/java/io/InputStream.html#available()) zu verwenden, um einen Puffer für die Gesamtheit eines Streams. – Jeffrey

+0

@ Jeffrey Wenn Sie es haben, würde ich gerne alle Ressourcen sehen, die Sie dazu haben. Zuvor hatte ich das Gerät ziemlich glücklich benutzt, ohne irgendwelche Probleme zu haben. Ich glaube Ihnen, aber ich frage mich, ob es wirklich eine Situation gibt, in der die Verfügbarkeit angemessen ist. – Jeremy

3

FileReader wird normalerweise in Verbindung mit einem BufferedReader verwendet, da es häufig sinnvoll ist, eine Datei zeilenweise zu lesen, besonders wenn die Datei eine wohldefinierte Datensatzstruktur hat, in der jeder Datensatz einer Zeile entspricht.

Auch FileReader kann für den Umgang mit Zeichenkodierungen und Conversions einen Teil der Arbeit vereinfachen, wie in den javadocs erklärte:

Convenience Klassencharakter Dateien zum Lesen. Die Konstruktoren dieser Klasse setzen voraus, dass die Standardzeichencodierung und die Standard-Bytepuffergröße geeignet sind. FileReader ist zum Lesen von Zeichenströmen gedacht.

3

Versuchen Sie, BufferedReader Puffergröße zu erhöhen. Zum Beispiel:

BufferedReader br = new BufferedReader(new FileReader("test"),2000000); 

Wenn Sie die richtige Puffergröße wählen, werden Sie schneller sein.

Dann in Ihrer Probe mit Reader verbringen Sie Zeit mit der Füllung des StringBuilder. Sie müssen die Datei Zeile für Zeile lesen, wenn Sie Zeilen verarbeiten müssen. Aber wenn Sie nur einen Text in einer Zeichenkette lesen müssen, dann lesen Sie einen größeren Textblock mit public int read(char[] cbuf) und schreiben Sie die Chunks in eine StringWriter initialisiert mit einer richtigen Größe.

Wählen Sie InputStream oder Reader zu verwenden hängt nicht von der Leistung ab. Im Allgemeinen verwenden Sie Reader, wenn Sie Textdaten lesen, weil Sie mit dem Leser den Zeichensatz leichter handhaben können.

Ein weiterer Punkt, den Code hier

byte[] b = new byte[is.available()]; 
is.read(b); 
String text = new String(b); 

ist es nicht richtig. Die documentation sagt

Beachten Sie, dass einige Implementierungen von InputStream die Gesamtzahl der Bytes im Stream zurückgeben werden, viele nicht. Es ist niemals richtig, den Rückgabewert dieser Methode zu verwenden, um einen Puffer zuzuordnen, der alle Daten in diesem Stream enthalten soll.

also aufgepasst, Sie müssen es beheben.

+0

Die manuelle Bereitstellung einer Puffergröße schien sich nur negativ auf die Leistung für mich auszuwirken. – Jeremy

+0

Wie groß ist Ihre Datei? Wie viel Heap widmen Sie Ihrer JVM? – dash1e