Frage Eine Nur-Text-Datei in Java lesen


Es scheint, dass es verschiedene Möglichkeiten gibt, Daten von Dateien in Java zu lesen und zu schreiben.

Ich möchte ASCII-Daten aus einer Datei lesen. Was sind die möglichen Wege und ihre Unterschiede?


796
2018-01-17 18:29


Ursprung


Antworten:


ASCII ist eine TEXT-Datei, die Sie verwenden würden Leser zum Lesen. Java unterstützt auch das Lesen aus einer Binärdatei mit InputStreams. Wenn die zu lesenden Dateien sehr groß sind, sollten Sie a verwenden GepufferterReader oben auf einem Dateireader um die Leseleistung zu verbessern.

Durchgehen Dieser Artikel wie man einen Reader benutzt

Ich würde Ihnen auch empfehlen, dieses wundervolle (noch freie) Buch herunterzuladen und zu lesen In Java denken

In Java 7:

neue Zeichenfolge (Files.readAllBytes (...)) oder Files.readAllLines (...)

In Java 8:

Files.lines (..). ForEach (...)


467
2018-01-17 18:31



Meine Lieblingsmethode zum Lesen einer kleinen Datei ist die Verwendung eines BufferedReader und eines StringBuilders. Es ist sehr einfach und auf den Punkt (obwohl nicht besonders effektiv, aber gut genug für die meisten Fälle):

BufferedReader br = new BufferedReader(new FileReader("file.txt"));
try {
    StringBuilder sb = new StringBuilder();
    String line = br.readLine();

    while (line != null) {
        sb.append(line);
        sb.append(System.lineSeparator());
        line = br.readLine();
    }
    String everything = sb.toString();
} finally {
    br.close();
}

Einige haben darauf hingewiesen, dass Sie nach Java 7 verwenden sollten Versuche mit Ressourcen (d. h. automatisch schließen) Merkmale:

try(BufferedReader br = new BufferedReader(new FileReader("file.txt"))) {
    StringBuilder sb = new StringBuilder();
    String line = br.readLine();

    while (line != null) {
        sb.append(line);
        sb.append(System.lineSeparator());
        line = br.readLine();
    }
    String everything = sb.toString();
}

Wenn ich solche Strings lese, möchte ich normalerweise eine String-Behandlung pro Zeile machen, also gehe ich auf diese Implementierung.

Obwohl ich eigentlich nur eine Datei in einen String lesen möchte, benutze ich immer Apache Commons IO mit der Klasse IOUtils.toString () -Methode. Sie können sich die Quelle hier ansehen:

http://www.docjar.com/html/api/org/apache/commons/io/IOUtils.java.html

FileInputStream inputStream = new FileInputStream("foo.txt");
try {
    String everything = IOUtils.toString(inputStream);
} finally {
    inputStream.close();
}

Und noch einfacher mit Java 7:

try(FileInputStream inputStream = new FileInputStream("foo.txt")) {     
    String everything = IOUtils.toString(inputStream);
    // do something with everything string
}

634
2018-01-17 18:42



Der einfachste Weg ist die Verwendung der Scanner Klasse in Java und das FileReader-Objekt. Einfaches Beispiel:

Scanner in = new Scanner(new FileReader("filename.txt"));

Scanner verfügt über mehrere Methoden zum Lesen von Strings, Zahlen usw. Sie können auf der Java-Dokumentationsseite nach weiteren Informationen suchen.

Zum Beispiel das Lesen des gesamten Inhalts in ein String:

StringBuilder sb = new StringBuilder();
while(in.hasNext()) {
    sb.append(in.next());
}
in.close();
outString = sb.toString();

Auch wenn Sie eine bestimmte Kodierung benötigen, können Sie diese anstelle von verwenden FileReader:

new InputStreamReader(new FileInputStream(fileUtf8), StandardCharsets.UTF_8)

122
2018-01-17 18:35



Hier ist eine einfache Lösung:

String content;

content = new String(Files.readAllBytes(Paths.get("sample.txt")));

59
2018-01-29 16:24



Hier ist ein anderer Weg, es ohne externe Bibliotheken zu tun:

import java.io.File;
import java.io.FileReader;
import java.io.IOException;

public String readFile(String filename)
{
    String content = null;
    File file = new File(filename); // For example, foo.txt
    FileReader reader = null;
    try {
        reader = new FileReader(file);
        char[] chars = new char[(int) file.length()];
        reader.read(chars);
        content = new String(chars);
        reader.close();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if(reader != null){
            reader.close();
        }
    }
    return content;
}

54
2018-05-22 21:02



Die Methoden innerhalb org.apache.commons.io.FileUtils kann auch sehr praktisch sein, z.B.

/**
 * Reads the contents of a file line by line to a List
 * of Strings using the default encoding for the VM.
 */
static List readLines(File file)

21
2018-01-17 18:46



Ich musste die verschiedenen Wege benchmarken. Ich werde meine Ergebnisse kommentieren, aber kurz gesagt, der schnellste Weg ist die Verwendung eines einfachen alten BufferedInputStream über einen FileInputStream. Wenn viele Dateien gelesen werden müssen, reduzieren drei Threads die Gesamtausführungszeit auf ungefähr die Hälfte, aber das Hinzufügen von mehr Threads verschlechtert die Leistung progressiv, bis es mit zwanzig Threads dreimal länger dauert als mit nur einem Thread.

Die Annahme ist, dass Sie eine Datei lesen und mit ihrem Inhalt etwas Sinnvolles tun müssen. In den Beispielen lesen Sie Zeilen aus einem Protokoll und zählen diejenigen, die Werte enthalten, die einen bestimmten Schwellenwert überschreiten. Also ich gehe davon aus, dass der One-Liner Java 8 Files.lines(Paths.get("/path/to/file.txt")).map(line -> line.split(";")) ist keine Option.

Ich habe auf Java 1.8, Windows 7 und sowohl SSD als auch HDD-Laufwerken getestet.

Ich habe sechs verschiedene Implementierungen geschrieben:

rawParse: Verwenden Sie BufferedInputStream über einen FileInputStream, und schneiden Sie dann Zeilen aus, die byteweise gelesen werden. Dies hat alle anderen Single-Thread-Ansätze übertroffen, ist aber für Nicht-ASCII-Dateien möglicherweise sehr unpraktisch.

lineReaderParsen: Verwenden Sie einen BufferedReader über einen FileReader, lesen Sie Zeile für Zeile, trennen Sie Zeilen, indem Sie String.split () aufrufen. Dies ist ungefähr 20% langsamer als rawParse.

lineReaderParseParallel: Dies ist dasselbe wie lineReaderParse, aber es verwendet mehrere Threads. Dies ist in allen Fällen die schnellste Option.

nioFilesParsen: Verwenden Sie java.nio.files.Files.lines ()

nioAsyncParse: Verwenden Sie einen AsynchronousFileChannel mit einem Beendigungshandler und einem Thread-Pool.

nioMemoryMappedParse: Verwenden Sie eine speicherdefinierte Datei. Dies ist wirklich eine schlechte Idee, die Ausführungszeiten mindestens drei Mal länger als jede andere Implementierung liefert.

Dies sind die durchschnittlichen Zeiten für das Lesen von 204 Dateien mit jeweils 4 MB auf einem Quad-Core-i7- und SSD-Laufwerk. Die Dateien werden im laufenden Betrieb generiert, um ein Disk-Caching zu vermeiden.

rawParse                11.10 sec
lineReaderParse         13.86 sec
lineReaderParseParallel  6.00 sec
nioFilesParse           13.52 sec
nioAsyncParse           16.06 sec
nioMemoryMappedParse    37.68 sec

Ich fand einen Unterschied kleiner als ich erwartet hatte zwischen dem Laufen auf einer SSD oder einem Festplattenlaufwerk, das die SSD ungefähr 15% schneller ist. Dies liegt möglicherweise daran, dass die Dateien auf einer unfragmentierten Festplatte erstellt und sequenziell gelesen werden. Daher kann das drehende Laufwerk fast wie eine SSD ausgeführt werden.

Ich war überrascht von der geringen Leistung der nioAsyncParse-Implementierung. Entweder habe ich etwas falsch implementiert oder die Multithread-Implementierung mit NIO und ein Completion-Handler führt dasselbe (oder sogar schlechter) aus als eine Single-Thread-Implementierung mit der java.io-API. Darüber hinaus ist das asynchrone Parsen mit einem CompletionHandler in Codezeilen viel länger und schwierig zu implementieren als eine direkte Implementierung in alten Streams.

Nun folgt auf die sechs Implementierungen eine Klasse, die sie alle enthält, sowie eine parametrisierbare main () -Methode, die es erlaubt, mit der Anzahl der Dateien, der Dateigröße und dem Grad der Nebenläufigkeit zu spielen. Beachten Sie, dass die Größe der Dateien plus minus 20% variiert. Dies verhindert, dass alle Dateien genau gleich groß sind.

rawParse

public void rawParse(final String targetDir, final int numberOfFiles) throws IOException, ParseException {
    overrunCount = 0;
    final int dl = (int) ';';
    StringBuffer lineBuffer = new StringBuffer(1024);
    for (int f=0; f<numberOfFiles; f++) {
        File fl = new File(targetDir+filenamePreffix+String.valueOf(f)+".txt");
        FileInputStream fin = new FileInputStream(fl);
        BufferedInputStream bin = new BufferedInputStream(fin);
        int character;
        while((character=bin.read())!=-1) {
            if (character==dl) {

                // Here is where something is done with each line
                doSomethingWithRawLine(lineBuffer.toString());
                lineBuffer.setLength(0);
            }
            else {
                lineBuffer.append((char) character);
            }
        }
        bin.close();
        fin.close();
    }
}

public final void doSomethingWithRawLine(String line) throws ParseException {
    // What to do for each line
    int fieldNumber = 0;
    final int len = line.length();
    StringBuffer fieldBuffer = new StringBuffer(256);
    for (int charPos=0; charPos<len; charPos++) {
        char c = line.charAt(charPos);
        if (c==DL0) {
            String fieldValue = fieldBuffer.toString();
            if (fieldValue.length()>0) {
                switch (fieldNumber) {
                    case 0:
                        Date dt = fmt.parse(fieldValue);
                        fieldNumber++;
                        break;
                    case 1:
                        double d = Double.parseDouble(fieldValue);
                        fieldNumber++;
                        break;
                    case 2:
                        int t = Integer.parseInt(fieldValue);
                        fieldNumber++;
                        break;
                    case 3:
                        if (fieldValue.equals("overrun"))
                            overrunCount++;
                        break;
                }
            }
            fieldBuffer.setLength(0);
        }
        else {
            fieldBuffer.append(c);
        }
    }
}

lineReaderParsen

public void lineReaderParse(final String targetDir, final int numberOfFiles) throws IOException, ParseException {
    String line;
    for (int f=0; f<numberOfFiles; f++) {
        File fl = new File(targetDir+filenamePreffix+String.valueOf(f)+".txt");
        FileReader frd = new FileReader(fl);
        BufferedReader brd = new BufferedReader(frd);

        while ((line=brd.readLine())!=null)
            doSomethingWithLine(line);
        brd.close();
        frd.close();
    }
}

public final void doSomethingWithLine(String line) throws ParseException {
    // Example of what to do for each line
    String[] fields = line.split(";");
    Date dt = fmt.parse(fields[0]);
    double d = Double.parseDouble(fields[1]);
    int t = Integer.parseInt(fields[2]);
    if (fields[3].equals("overrun"))
        overrunCount++;
}

lineReaderParseParallel

public void lineReaderParseParallel(final String targetDir, final int numberOfFiles, final int degreeOfParalelism) throws IOException, ParseException, InterruptedException {
    Thread[] pool = new Thread[degreeOfParalelism];
    int batchSize = numberOfFiles / degreeOfParalelism;
    for (int b=0; b<degreeOfParalelism; b++) {
        pool[b] = new LineReaderParseThread(targetDir, b*batchSize, b*batchSize+b*batchSize);
        pool[b].start();
    }
    for (int b=0; b<degreeOfParalelism; b++)
        pool[b].join();
}

class LineReaderParseThread extends Thread {

    private String targetDir;
    private int fileFrom;
    private int fileTo;
    private DateFormat fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    private int overrunCounter = 0;

    public LineReaderParseThread(String targetDir, int fileFrom, int fileTo) {
        this.targetDir = targetDir;
        this.fileFrom = fileFrom;
        this.fileTo = fileTo;
    }

    private void doSomethingWithTheLine(String line) throws ParseException {
        String[] fields = line.split(DL);
        Date dt = fmt.parse(fields[0]);
        double d = Double.parseDouble(fields[1]);
        int t = Integer.parseInt(fields[2]);
        if (fields[3].equals("overrun"))
            overrunCounter++;
    }

    @Override
    public void run() {
        String line;
        for (int f=fileFrom; f<fileTo; f++) {
            File fl = new File(targetDir+filenamePreffix+String.valueOf(f)+".txt");
            try {
            FileReader frd = new FileReader(fl);
            BufferedReader brd = new BufferedReader(frd);
            while ((line=brd.readLine())!=null) {
                doSomethingWithTheLine(line);
            }
            brd.close();
            frd.close();
            } catch (IOException | ParseException ioe) { }
        }
    }
}

nioFilesParsen

public void nioFilesParse(final String targetDir, final int numberOfFiles) throws IOException, ParseException {
    for (int f=0; f<numberOfFiles; f++) {
        Path ph = Paths.get(targetDir+filenamePreffix+String.valueOf(f)+".txt");
        Consumer<String> action = new LineConsumer();
        Stream<String> lines = Files.lines(ph);
        lines.forEach(action);
        lines.close();
    }
}


class LineConsumer implements Consumer<String> {

    @Override
    public void accept(String line) {

        // What to do for each line
        String[] fields = line.split(DL);
        if (fields.length>1) {
            try {
                Date dt = fmt.parse(fields[0]);
            }
            catch (ParseException e) {
            }
            double d = Double.parseDouble(fields[1]);
            int t = Integer.parseInt(fields[2]);
            if (fields[3].equals("overrun"))
                overrunCount++;
        }
    }
}

nioAsyncParse

public void nioAsyncParse(final String targetDir, final int numberOfFiles, final int numberOfThreads, final int bufferSize) throws IOException, ParseException, InterruptedException {
    ScheduledThreadPoolExecutor pool = new ScheduledThreadPoolExecutor(numberOfThreads);
    ConcurrentLinkedQueue<ByteBuffer> byteBuffers = new ConcurrentLinkedQueue<ByteBuffer>();

    for (int b=0; b<numberOfThreads; b++)
        byteBuffers.add(ByteBuffer.allocate(bufferSize));

    for (int f=0; f<numberOfFiles; f++) {
        consumerThreads.acquire();
        String fileName = targetDir+filenamePreffix+String.valueOf(f)+".txt";
        AsynchronousFileChannel channel = AsynchronousFileChannel.open(Paths.get(fileName), EnumSet.of(StandardOpenOption.READ), pool);
        BufferConsumer consumer = new BufferConsumer(byteBuffers, fileName, bufferSize);
        channel.read(consumer.buffer(), 0l, channel, consumer);
    }
    consumerThreads.acquire(numberOfThreads);
}


class BufferConsumer implements CompletionHandler<Integer, AsynchronousFileChannel> {

        private ConcurrentLinkedQueue<ByteBuffer> buffers;
        private ByteBuffer bytes;
        private String file;
        private StringBuffer chars;
        private int limit;
        private long position;
        private DateFormat frmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        public BufferConsumer(ConcurrentLinkedQueue<ByteBuffer> byteBuffers, String fileName, int bufferSize) {
            buffers = byteBuffers;
            bytes = buffers.poll();
            if (bytes==null)
                bytes = ByteBuffer.allocate(bufferSize);

            file = fileName;
            chars = new StringBuffer(bufferSize);
            frmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            limit = bufferSize;
            position = 0l;
        }

        public ByteBuffer buffer() {
            return bytes;
        }

        @Override
        public synchronized void completed(Integer result, AsynchronousFileChannel channel) {

            if (result!=-1) {
                bytes.flip();
                final int len = bytes.limit();
                int i = 0;
                try {
                    for (i = 0; i < len; i++) {
                        byte by = bytes.get();
                        if (by=='\n') {
                            // ***
                            // The code used to process the line goes here
                            chars.setLength(0);
                        }
                        else {
                                chars.append((char) by);
                        }
                    }
                }
                catch (Exception x) {
                    System.out.println(
                        "Caught exception " + x.getClass().getName() + " " + x.getMessage() +
                        " i=" + String.valueOf(i) + ", limit=" + String.valueOf(len) +
                        ", position="+String.valueOf(position));
                }

                if (len==limit) {
                    bytes.clear();
                    position += len;
                    channel.read(bytes, position, channel, this);
                }
                else {
                    try {
                        channel.close();
                    }
                    catch (IOException e) {
                    }
                    consumerThreads.release();
                    bytes.clear();
                    buffers.add(bytes);
                }
            }
            else {
                try {
                    channel.close();
                }
                catch (IOException e) {
                }
                consumerThreads.release();
                bytes.clear();
                buffers.add(bytes);
            }
        }

        @Override
        public void failed(Throwable e, AsynchronousFileChannel channel) {
        }
};

VOLLE RUNNABLE UMSETZUNG ALLER FÄLLE

https://github.com/sergiomt/javaiobenchmark/blob/master/FileReadBenchmark.java


21
2017-11-14 20:20



Hier sind die drei arbeitenden und getesteten Methoden:

Verwenden BufferedReader

package io;
import java.io.*;
public class ReadFromFile2 {
    public static void main(String[] args)throws Exception {
        File file = new File("C:\\Users\\pankaj\\Desktop\\test.java");
        BufferedReader br = new BufferedReader(new FileReader(file));
        String st;
        while((st=br.readLine()) != null){
            System.out.println(st);
        }
    }
}

Verwenden Scanner

package io;

import java.io.File;
import java.util.Scanner;

public class ReadFromFileUsingScanner {
    public static void main(String[] args) throws Exception {
        File file = new File("C:\\Users\\pankaj\\Desktop\\test.java");
        Scanner sc = new Scanner(file);
        while(sc.hasNextLine()){
            System.out.println(sc.nextLine());
        }
    }
}

Verwenden FileReader

package io;
import java.io.*;
public class ReadingFromFile {

    public static void main(String[] args) throws Exception {
        FileReader fr = new FileReader("C:\\Users\\pankaj\\Desktop\\test.java");
        int i;
        while ((i=fr.read()) != -1){
            System.out.print((char) i);
        }
    }
}

Lesen Sie die gesamte Datei ohne eine Schleife mit dem Scanner Klasse

package io;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;

public class ReadingEntireFileWithoutLoop {

    public static void main(String[] args) throws FileNotFoundException {
        File file = new File("C:\\Users\\pankaj\\Desktop\\test.java");
        Scanner sc = new Scanner(file);
        sc.useDelimiter("\\Z");
        System.out.println(sc.next());
    }
}

19
2018-01-10 18:52