it-swarm.com.de

Der schnellste Weg zum Lesen und Schreiben großer Dateien Zeile für Zeile in Java

Ich habe viel nach dem schnellsten Weg gesucht, große Dateien (0,5 - 1 GB) mit begrenztem Speicher (ca. 64 MB) in Java zu lesen und erneut zu schreiben. Jede Zeile in der Datei stellt einen Datensatz dar, daher muss ich sie Zeile für Zeile abrufen. Die Datei ist eine normale Textdatei.

Ich habe BufferedReader und BufferedWriter ausprobiert, aber es scheint nicht die beste Option zu sein. Das Lesen und Schreiben einer Datei mit einer Größe von 0,5 GB dauert etwa 35 Sekunden. Nur Lesen, Schreiben ohne Verarbeitung. Ich denke, der Engpass hier ist das Schreiben, da das Lesen alleine etwa 10 Sekunden dauert.

Ich habe versucht, ein Array von Bytes zu lesen, aber das Suchen nach Zeilen in jedem Array, das gelesen wurde, dauert länger.

Anregungen bitte? Danke

18
user1785771

Ich vermute, Ihr eigentliches Problem ist, dass Sie nur über eine begrenzte Hardware verfügen und dass die Software nicht viel Unterschied macht. Wenn Sie über genügend Arbeitsspeicher und CPU verfügen, können fortgeschrittene Tricks hilfreich sein. Wenn Sie jedoch nur auf Ihrer Festplatte warten, weil die Datei nicht zwischengespeichert wird, ist dies kein großer Unterschied.

BTW: 500 MB in 10 Sekunden oder 50 MB/Sekunde sind eine typische Lesegeschwindigkeit für eine Festplatte.

Führen Sie die folgenden Schritte aus, um festzustellen, wann Ihr System die Datei nicht effizient zwischenspeichern kann.

public static void main(String... args) throws IOException {
    for (int mb : new int[]{50, 100, 250, 500, 1000, 2000})
        testFileSize(mb);
}

private static void testFileSize(int mb) throws IOException {
    File file = File.createTempFile("test", ".txt");
    file.deleteOnExit();
    char[] chars = new char[1024];
    Arrays.fill(chars, 'A');
    String longLine = new String(chars);
    long start1 = System.nanoTime();
    PrintWriter pw = new PrintWriter(new FileWriter(file));
    for (int i = 0; i < mb * 1024; i++)
        pw.println(longLine);
    pw.close();
    long time1 = System.nanoTime() - start1;
    System.out.printf("Took %.3f seconds to write to a %d MB, file rate: %.1f MB/s%n",
            time1 / 1e9, file.length() >> 20, file.length() * 1000.0 / time1);

    long start2 = System.nanoTime();
    BufferedReader br = new BufferedReader(new FileReader(file));
    for (String line; (line = br.readLine()) != null; ) {
    }
    br.close();
    long time2 = System.nanoTime() - start2;
    System.out.printf("Took %.3f seconds to read to a %d MB file, rate: %.1f MB/s%n",
            time2 / 1e9, file.length() >> 20, file.length() * 1000.0 / time2);
    file.delete();
}

Auf einem Linux-Computer mit viel Speicher.

Took 0.395 seconds to write to a 50 MB, file rate: 133.0 MB/s
Took 0.375 seconds to read to a 50 MB file, rate: 140.0 MB/s
Took 0.669 seconds to write to a 100 MB, file rate: 156.9 MB/s
Took 0.569 seconds to read to a 100 MB file, rate: 184.6 MB/s
Took 1.585 seconds to write to a 250 MB, file rate: 165.5 MB/s
Took 1.274 seconds to read to a 250 MB file, rate: 206.0 MB/s
Took 2.513 seconds to write to a 500 MB, file rate: 208.8 MB/s
Took 2.332 seconds to read to a 500 MB file, rate: 225.1 MB/s
Took 5.094 seconds to write to a 1000 MB, file rate: 206.0 MB/s
Took 5.041 seconds to read to a 1000 MB file, rate: 208.2 MB/s
Took 11.509 seconds to write to a 2001 MB, file rate: 182.4 MB/s
Took 9.681 seconds to read to a 2001 MB file, rate: 216.8 MB/s

Auf einem Windows-Computer mit viel Speicher.

Took 0.376 seconds to write to a 50 MB, file rate: 139.7 MB/s
Took 0.401 seconds to read to a 50 MB file, rate: 131.1 MB/s
Took 0.517 seconds to write to a 100 MB, file rate: 203.1 MB/s
Took 0.520 seconds to read to a 100 MB file, rate: 201.9 MB/s
Took 1.344 seconds to write to a 250 MB, file rate: 195.4 MB/s
Took 1.387 seconds to read to a 250 MB file, rate: 189.4 MB/s
Took 2.368 seconds to write to a 500 MB, file rate: 221.8 MB/s
Took 2.454 seconds to read to a 500 MB file, rate: 214.1 MB/s
Took 4.985 seconds to write to a 1001 MB, file rate: 210.7 MB/s
Took 5.132 seconds to read to a 1001 MB file, rate: 204.7 MB/s
Took 10.276 seconds to write to a 2003 MB, file rate: 204.5 MB/s
Took 9.964 seconds to read to a 2003 MB file, rate: 210.9 MB/s
15
Peter Lawrey

Als erstes würde ich versuchen, die Puffergröße von BufferedReader und BufferedWriter zu erhöhen. Die Standardpuffergrößen sind nicht dokumentiert, aber im Oracle VM sind dies 8192 Zeichen, was keinen großen Leistungsvorteil bringt. 

Wenn Sie nur eine Kopie der Datei erstellen müssen (und keinen tatsächlichen Zugriff auf die Daten benötigen), würde ich entweder den Reader/Writer-Ansatz verwerfen und direkt mit InputStream und OutputStream arbeiten, wobei ein Byte-Array als Puffer verwendet wird:

FileInputStream fis = new FileInputStream("d:/test.txt");
FileOutputStream fos = new FileOutputStream("d:/test2.txt");
byte[] b = new byte[bufferSize];
int r;
while ((r=fis.read(b))>=0) {
    fos.write(b, 0, r);         
}
fis.close();
fos.close();

oder tatsächlich NIO verwenden:

FileChannel in = new RandomAccessFile("d:/test.txt", "r").getChannel();
FileChannel out = new RandomAccessFile("d:/test2.txt", "rw").getChannel();
out.transferFrom(in, 0, Long.MAX_VALUE);
in.close();
out.close();

Beim Benchmarking der verschiedenen Kopiermethoden habe ich jedoch viel größere Unterschiede (Dauer) zwischen den einzelnen Durchläufen des Benchmarks als zwischen den verschiedenen Implementierungen. I/O-Caching (sowohl auf Betriebssystemebene als auch auf dem Festplatten-Cache) spielt hier eine große Rolle und es ist sehr schwer zu sagen, was schneller ist. Auf meiner Hardware dauert das Kopieren einer 1-GB-Textdatei mit BufferedReader und BufferedWriter Zeile für Zeile weniger als 5 Sekunden und in anderen Ausführungen mehr als 30 Sekunden.

9
jarnbjo

In Java 7 können Sie die Methoden Files.readAllLines () und Files.write () verwenden. Hier ist das Beispiel:

List<String> readTextFile(String fileName) throws IOException {
    Path path = Paths.get(fileName);
    return Files.readAllLines(path, StandardCharsets.UTF_8);
}

void writeTextFile(List<String> strLines, String fileName) throws IOException {
    Path path = Paths.get(fileName);
    Files.write(path, strLines, StandardCharsets.UTF_8);
}
4
Oleg K

Ich würde empfehlen, die Klassen im Java.nio-Paket anzusehen. Nicht blockierendes IO kann für Sockets schneller sein:

http://docs.Oracle.com/javase/6/docs/api/Java/nio/package-summary.html

Dieser Artikel enthält Benchmarks, die besagen, dass es wahr ist:

http://vanillajava.blogspot.com/2010/07/Java-nio-is-faster-than-Java-io-for.html

2
duffymo

Alles dreht sich um OutOfMemoryException, das mit dem Scanner-Klasseniterator effizient verarbeitet werden kann. Es liest die Datei zeilenweise und nicht in großen Mengen.

Der folgende Code behebt das Problem:

try(FileInputStream inputStream =new FileInputStream("D:\\File\\test.txt");
  Scanner sc= new Scanner(inputStream, "UTF-8")) {
  while (sc.hasNextLine()) {
    String line = sc.nextLine();
    System.out.println(line);
  }
} catch (IOException e) {
  e.printStackTrace();
}
0
vkstream

Ich habe einen ausführlichen Artikel über die vielen Möglichkeiten geschrieben, Lesen von Dateien in Java zu testen und sie mit Beispieldateien von 1 KB bis 1 GB gegeneinander zu testen, und ich habe festgestellt, dass die folgenden 3 Methoden am schnellsten zum Lesen von 1 GB-Dateien waren :

1) Java.nio.file.Files.readAllBytes () - Es dauerte knapp 1 Sekunde, um eine 1 GB-Testdatei zu lesen.

import Java.io.File;
import Java.io.IOException;
import Java.nio.file.Files;

public class ReadFile_Files_ReadAllBytes {
  public static void main(String [] pArgs) throws IOException {
    String fileName = "c:\\temp\\sample-10KB.txt";
    File file = new File(fileName);

    byte [] fileBytes = Files.readAllBytes(file.toPath());
    char singleChar;
    for(byte b : fileBytes) {
      singleChar = (char) b;
      System.out.print(singleChar);
    }
  }
}

2) Java.nio.file.Files.lines () - Es dauerte etwa 3,5 Sekunden, um eine Testdatei von 1 GB einzulesen.

import Java.io.File;
import Java.io.IOException;
import Java.nio.file.Files;
import Java.util.stream.Stream;

public class ReadFile_Files_Lines {
  public static void main(String[] pArgs) throws IOException {
    String fileName = "c:\\temp\\sample-10KB.txt";
    File file = new File(fileName);

    try (Stream linesStream = Files.lines(file.toPath())) {
      linesStream.forEach(line -&gt; {
        System.out.println(line);
      });
    }
  }
}

3) Java.io.BufferedReader - Das Lesen einer 1 GB-Testdatei dauerte etwa 4,5 Sekunden.

import Java.io.BufferedReader;
import Java.io.FileReader;
import Java.io.IOException;

public class ReadFile_BufferedReader_ReadLine {
  public static void main(String [] args) throws IOException {
    String fileName = "c:\\temp\\sample-10KB.txt";
    FileReader fileReader = new FileReader(fileName);

    try (BufferedReader bufferedReader = new BufferedReader(fileReader)) {
      String line;
      while((line = bufferedReader.readLine()) != null) {
        System.out.println(line);
      }
    }
  }
}
0
gomisha