Wie erstelle ich eine Java-Zeichenfolge aus dem Inhalt einer Datei?

1513

Ich benutze die Redewendung unten schon seit einiger Zeit. Und es scheint am weitesten verbreitet zu sein, zumindest auf den Websites, die ich besucht habe.

Gibt es eine bessere / andere Möglichkeit, eine Datei in Java in eine Zeichenfolge einzulesen?

private String readFile(String file) throws IOException {
    BufferedReader reader = new BufferedReader(new FileReader (file));
    String         line = null;
    StringBuilder  stringBuilder = new StringBuilder();
    String         ls = System.getProperty("line.separator");

    try {
        while((line = reader.readLine()) != null) {
            stringBuilder.append(line);
            stringBuilder.append(ls);
        }

        return stringBuilder.toString();
    } finally {
        reader.close();
    }
}
OscarRyz
quelle
7
Kann mir jemand auf sehr einfache Weise erklären, was mit dem NIO ist? Jedes Mal, wenn ich darüber lese, verliere ich mich in der n-ten Erwähnung des Kanals :(
OscarRyz
7
Denken Sie daran, dass nicht garantiert werden kann, dass das Zeilentrennzeichen in der Datei nicht mit dem Zeilentrennzeichen des Systems identisch ist.
Henrik Paul
138
Könnten Sie bitte endlich einen richtigen Versuch einfügen, der den Leser schließt? Jemand könnte dieses Beispiel tatsächlich verwenden und einen Fehler in seinen Code einfügen.
Hans-Peter Störr
6
Der obige Code hat den Fehler, dass in der letzten Zeile ein zusätzliches neues Zeilenzeichen hinzugefügt wird. Es sollte ungefähr so ​​lauten: if (line = reader.readLine ())! = Null) {stringBuilder.append (line); } while (line = reader.readLine ())! = null) {stringBuilder.append (ls); stringBuilder.append (Zeile); }
Deep
27
Einführung in Java 7 byte[] Files.readAllBytes(file);Für diejenigen, die die einzeilige Scannerlösung vorschlagen: Müssen Sie sie nicht schließen?
Val

Antworten:

1535

Lesen Sie den gesamten Text aus einer Datei

Java 11 hat die Methode readString () hinzugefügt , um kleine Dateien als Zeilenabschlusszeichen zu lesen String:

String content = Files.readString(path, StandardCharsets.US_ASCII);

Für Versionen zwischen Java 7 und 11 ist hier eine kompakte, robuste Sprache, die in einer Dienstprogrammmethode zusammengefasst ist:

static String readFile(String path, Charset encoding) 
  throws IOException 
{
  byte[] encoded = Files.readAllBytes(Paths.get(path));
  return new String(encoded, encoding);
}

Lesen Sie Textzeilen aus einer Datei

Java 7 hat eine bequeme Methode zum Lesen einer Datei als Textzeilen hinzugefügt , die als dargestellt wird List<String>. Dieser Ansatz ist "verlustbehaftet", da die Zeilentrennzeichen vom Ende jeder Zeile entfernt werden.

List<String> lines = Files.readAllLines(Paths.get(path), encoding);

Java 8 fügte die Files.lines()Methode hinzu , um a zu erzeugen Stream<String>. Auch diese Methode ist verlustbehaftet, da Zeilentrennzeichen entfernt werden. Wenn IOExceptionbeim Lesen der Datei ein auftritt , wird es in ein eingeschlossen UncheckedIOException, da Streamkeine Lambdas akzeptiert werden, die geprüfte Ausnahmen auslösen.

try (Stream<String> lines = Files.lines(path, encoding)) {
  lines.forEach(System.out::println);
}

Dies Streamerfordert einen close()Anruf; Dies ist in der API schlecht dokumentiert, und ich vermute, dass viele Leute nicht einmal bemerken, dass Streames eine gibtclose() Methode gibt. Stellen Sie sicher, dass Sie wie gezeigt einen ARM-Block verwenden.

Wenn Sie mit einer anderen Quelle als einer Datei arbeiten, können Sie die lines()Methode in verwendenBufferedReader stattdessen verwenden.

Speicherauslastung

Die erste Methode, bei der Zeilenumbrüche beibehalten werden, kann vorübergehend Speicherplatz benötigen, der um ein Vielfaches größer ist als die Datei, da für kurze Zeit der Inhalt der Rohdatei (ein Byte-Array) und die decodierten Zeichen (von denen jedes 16 Bit beträgt, selbst wenn es codiert ist) als 8 Bits in der Datei) befinden sich gleichzeitig im Speicher. Es ist am sichersten, auf Dateien anzuwenden, von denen Sie wissen, dass sie im Verhältnis zum verfügbaren Speicher klein sind.

Die zweite Methode, das Lesen von Zeilen, ist normalerweise speichereffizienter, da der Eingabebytepuffer zum Decodieren nicht die gesamte Datei enthalten muss. Es ist jedoch immer noch nicht für Dateien geeignet, die im Verhältnis zum verfügbaren Speicher sehr groß sind.

Zum Lesen großer Dateien benötigen Sie ein anderes Design für Ihr Programm, das einen Textblock aus einem Stream liest, verarbeitet und dann zum nächsten übergeht, wobei derselbe Speicherblock mit fester Größe wiederverwendet wird. Hier hängt "groß" von den Computerspezifikationen ab. Heutzutage kann dieser Schwellenwert viele Gigabyte RAM betragen. Die dritte Methode, a zu verwenden, Stream<String>ist eine Möglichkeit, dies zu tun, wenn Ihre eingegebenen "Datensätze" zufällig einzelne Zeilen sind. (Mit der readLine()Methode vonBufferedReader ist das prozedurale Äquivalent zu diesem Ansatz.)

Zeichenkodierung

Eine Sache, die im Beispiel im ursprünglichen Beitrag fehlt, ist die Zeichenkodierung. Es gibt einige Sonderfälle, in denen die Plattform standardmäßig Ihren Wünschen entspricht, diese sind jedoch selten und Sie sollten in der Lage sein, Ihre Wahl zu rechtfertigen.

Die StandardCharsetsKlasse definiert einige Konstanten für die Codierungen, die für alle Java-Laufzeiten erforderlich sind:

String content = readFile("test.txt", StandardCharsets.UTF_8);

Die Plattformvorgabe ist in der CharsetKlasse selbst verfügbar :

String content = readFile("test.txt", Charset.defaultCharset());

Hinweis: Diese Antwort ersetzt weitgehend meine Java 6-Version. Das Dienstprogramm von Java 7 vereinfacht den Code sicher, und die alte Antwort, die einen zugeordneten Bytepuffer verwendete, verhinderte, dass die gelesene Datei gelöscht wurde, bis der zugeordnete Puffer mit Müll gesammelt wurde. Sie können die alte Version über den Link "Bearbeitet" zu dieser Antwort anzeigen.

erickson
quelle
3
Technisch gesehen ist es O (n) in Zeit und Raum. Qualitativ ist es aufgrund der Unveränderlichkeitsanforderungen von Strings ziemlich schwer für das Gedächtnis; vorübergehend befinden sich zwei kopien der char-daten im speicher sowie der platz für die codierten bytes. Unter der Annahme einer Einzelbyte-Codierung werden (vorübergehend) 5 Byte Speicher für jedes Zeichen in der Datei benötigt. Da die Frage speziell nach einem String fragt, zeige ich dies. Wenn Sie jedoch mit dem von "decode" zurückgegebenen CharBuffer arbeiten können, ist der Speicherbedarf viel geringer. In Bezug auf die Zeit denke ich nicht, dass Sie in den Java-Kernbibliotheken etwas schnelleres finden werden.
Erickson
5
Möglicher Tippfehler? NIO hat eine Charset-Klasse (nicht CharSet) mit dem Namen java.nio.charset.Charset. Ist es das, was CharSet hätte sein sollen?
Jonathan Wright
31
Hinweis: Nachdem ich diesen Code ein wenig trainiert hatte, stellte ich fest, dass Sie die Datei nicht sofort nach dem Lesen mit dieser Methode zuverlässig löschen können. Dies ist in einigen Fällen möglicherweise kein Problem, aber nicht meine. Kann es im Zusammenhang mit diesem Problem stehen: bugs.sun.com/bugdatabase/view_bug.do?bug_id=4715154 ? Ich habe mich schließlich für Jon Skeet entschieden, der nicht unter diesem Fehler leidet. Wie auch immer, ich wollte nur die Informationen für andere Leute geben, nur für den Fall ...
Sébastien Nussbaumer
5
@ Sébastien Nussbaumer: Ich bin auch auf dieses Problem gestoßen. Erstaunlich, dass der Fehler als "Wird nicht behoben" markiert wurde. Dies bedeutet im Wesentlichen, dass dies FileChannel#mapim Allgemeinen unbrauchbar ist.
Joonas Pulakka
4
@ Sébastien Nussbaumer: Der Fehler wurde aus der Oracle / Sun Bug Database gelöscht: "Dieser Fehler ist nicht verfügbar." Google hat die Website unter webcache.googleusercontent.com/search?q=cache:bugs.sun.com/… zwischengespeichert
bobndrew
351

Wenn Sie bereit sind, eine externe Bibliothek zu verwenden, lesen Sie Apache Commons IO (200 KB JAR). Es enthält eine org.apache.commons.io.FileUtils.readFileToString()Methode, mit der Sie ein Ganzes mit einer Codezeile Filein eine lesen können String.

Beispiel:

import java.io.*;
import java.nio.charset.*;
import org.apache.commons.io.*;

public String readFile() throws IOException {
    File file = new File("data.txt");
    return FileUtils.readFileToString(file, StandardCharsets.UTF_8);
}
Willi aus Rohr
quelle
Ich finde diese Methode nicht in der von Ihnen angegebenen URL.
OscarRyz
2
Es ist in der Klasse org.apache.commons.io.FileUtils
Cyrille Ka
2
Ich verwende auch FileUtils, aber ich frage mich, was zwischen FileUtils oder der akzeptierten Nio-Antwort besser ist.
Guillaume
4
@ Guillaume: Die größte Frage ist, ob Sie eine Abhängigkeit von einer Bibliothek eines Drittanbieters haben möchten. Wenn Sie Commons IO oder Guava in Ihrem Projekt haben, verwenden Sie diese (nur zur Vereinfachung des Codes; andernfalls wird es wahrscheinlich keinen merklichen Unterschied geben).
Jonik
183

Eine sehr schlanke Lösung basierend auf Scanner:

Scanner scanner = new Scanner( new File("poem.txt") );
String text = scanner.useDelimiter("\\A").next();
scanner.close(); // Put this call in a finally block

Oder wenn Sie den Zeichensatz festlegen möchten:

Scanner scanner = new Scanner( new File("poem.txt"), "UTF-8" );
String text = scanner.useDelimiter("\\A").next();
scanner.close(); // Put this call in a finally block

Oder mit einem Try-with-Resources- Block, der scanner.close()Sie auffordert:

try (Scanner scanner = new Scanner( new File("poem.txt"), "UTF-8" )) {
    String text = scanner.useDelimiter("\\A").next();
}

Denken Sie daran, dass der ScannerKonstruktor ein werfen kann IOException. Und vergessen Sie nicht , zu importieren java.ioundjava.util .

Quelle: Pat Niemeyers Blog

Pablo Grisafi
quelle
4
\\ A funktioniert, weil es keinen "anderen Dateianfang" gibt, sodass Sie tatsächlich das letzte Token lesen ... das auch das erste ist. Nie mit \\ Z versucht. Beachten Sie auch, dass Sie alles lesen können, was lesbar ist, wie Dateien, InputStreams, Kanäle ... Ich verwende diesen Code manchmal, um aus dem Anzeigefenster von Eclipse zu lesen, wenn ich nicht sicher bin, ob ich die eine oder andere Datei lese. Ja, Klassenpfad verwirrt mich.
Pablo Grisafi
1
Als Poster kann ich sagen, dass ich wirklich nicht weiß, ob und wann die Datei richtig geschlossen ist ... Ich schreibe diese nie in Produktionscode, ich verwende sie nur für Tests oder Debugging.
Pablo Grisafi
2
Es hat ein Limit von 1024 Zeichen, denke ich
Whimusical
20
Der Scanner implementiert Closeable (er ruft nahe an der Quelle auf). Obwohl er elegant ist, sollte er eigentlich kein Einzeiler sein. Die Standardgröße des Puffers ist 1024, aber der Scanner erhöht die Größe nach Bedarf (siehe Scanner # makeSpace ())
earcam
8
Dieser schlägt bei leeren Dateien mit einem fehl java.util.NoSuchElementException.
SpaceTrucker
117
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;

String content = new String(Files.readAllBytes(Paths.get("readMe.txt")), StandardCharsets.UTF_8);

seit java 7 kannst du es so machen.

JoBⅈN
quelle
Dies sollte als Antwort akzeptiert werden - einzelne Zeile, keine externen Bibliotheken.
Cherry
Dies fügte am Ende ein Zeilenumbruchzeichen hinzu, auch wenn es nicht in der Datei vorhanden war
Stefan Haberl
79

Wenn Sie nach einer Alternative suchen, an der keine Bibliothek eines Drittanbieters beteiligt ist (z. B. Commons I / O ), können Sie die Scannerklasse verwenden :

private String readFile(String pathname) throws IOException {

    File file = new File(pathname);
    StringBuilder fileContents = new StringBuilder((int)file.length());        

    try (Scanner scanner = new Scanner(file)) {
        while(scanner.hasNextLine()) {
            fileContents.append(scanner.nextLine() + System.lineSeparator());
        }
        return fileContents.toString();
    }
}
Dónal
quelle
2
Ich denke, das ist der beste Weg. Überprüfen Sie heraus java.sun.com/docs/books/tutorial/essential/io/scanning.html
Tarski
3
Der Scannerkonstruktor, der eine Zeichenfolge akzeptiert, behandelt die Zeichenfolge nicht als Namen einer zu lesenden Datei, sondern als zu scannenden Text. Ich mache diesen Fehler die ganze Zeit. : - /
Alan Moore
@ Alan, guter Fang. Ich habe Dons Antwort leicht bearbeitet, um das zu beheben (ich hoffe).
Jonik
3
fileContents.append (scanner.nextLine ()). append (lineSeparator);
Ban-Geoengineering
1
Ändern Sie die Initialisierungsanweisung in Scanner scanner = new Scanner((Readable) new BufferedReader(new FileReader(file)));. Andernfalls können Sie nur einen Teil der Datei erfassen.
Wei Yang
71

Guave hat eine ähnliche Methode wie Commons IOUtils, die Willi aus Rohr erwähnt hat:

import com.google.common.base.Charsets;
import com.google.common.io.Files;

// ...

String text = Files.toString(new File(path), Charsets.UTF_8);

EDIT by PiggyPiglet
Files#toString ist veraltet und muss im Oktober 2019 entfernt werden. Verwenden Sie stattdessen Files.asCharSource(new File(path), StandardCharsets.UTF_8).read();

EDIT von Oscar Reyes

Dies ist der (vereinfachte) zugrunde liegende Code in der zitierten Bibliothek:

InputStream in = new FileInputStream(file);
byte[] b  = new byte[file.length()];
int len = b.length;
int total = 0;

while (total < len) {
  int result = in.read(b, total, len - total);
  if (result == -1) {
    break;
  }
  total += result;
}

return new String( b , Charsets.UTF_8 );

Bearbeiten (von Jonik): Das Obige stimmt nicht mit dem Quellcode der neuesten Guava-Versionen überein. Die aktuelle Quelle finden Sie in den Klassen Dateien , CharStreams , ByteSource und CharSource im Paket com.google.common.io .

OscarRyz
quelle
Dieser Code hat ein Casting von long nach int, was bei großen Dateien zu verrücktem Verhalten führen kann. Hat zusätzliche Leerzeichen und wo schließen Sie den Inputstream?
Mohamed Taher Alrefaie
@MTA: Der Stream ist geschlossen. Beachten Sie die Verwendung Closerin CharSource . Der Code in der Antwort ist nicht die aktuelle Guava-Quelle.
Jonik
54
import java.nio.file.Files;

....... .......

 String readFile(String filename) {
            File f = new File(filename);
            try {
                byte[] bytes = Files.readAllBytes(f.toPath());
                return new String(bytes,"UTF-8");
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return "";
    }
user590444
quelle
6
Oder noch einfacher:new String(Files.readAllBytes(FileSystems.getDefault().getPath( filename)));
12
oder new String(Files.readAllBytes(Paths.get(filename)));:-)
assafmo
1
Gut gespielt, und um den nächsten Kerl zu retten, Pathsist das Googeln anscheinend 1,7+ wie es ist FileSystems. (Dang it!)
Ruffin
4
Schade, dass diese Antwort nicht mehr Stimmen hat. Ich suchte nach dem schnellsten und einfachsten Weg, eine Textdatei in einen String zu bekommen. Das ist es und wenn ich nicht nach unten und unten und unten gescrollt hätte, hätte ich es verpasst. Das OP sollte erwägen, diese Antwort zu akzeptieren, um sie nach oben zu verschieben.
Dorn
@Thorn Diese Antwort hat schreckliche Fehlerbehandlung. Verwenden Sie diese Methode nicht im Produktionscode oder besser: niemals.
Xehpuk
51

Wenn Sie eine Zeichenfolgenverarbeitung (Parallelverarbeitung) benötigen, verfügt Java 8 über die großartige Stream-API.

String result = Files.lines(Paths.get("file.txt"))
                    .parallel() // for parallel processing 
                    .map(String::trim) // to change line   
                    .filter(line -> line.length() > 2) // to filter some lines by a predicate                        
                    .collect(Collectors.joining()); // to join lines

Weitere Beispiele finden Sie in JDK-Beispielen sample/lambda/BulkDataOperations, die von der Oracle Java SE 8-Downloadseite heruntergeladen werden können

Ein weiteres Beispiel für einen Einzeiler

String out = String.join("\n", Files.readAllLines(Paths.get("file.txt")));
Andrei N.
quelle
Kommt die .parallel () nach dem Lesen der Zeilen oder davor vor?
Istvan
Die eigentliche Arbeit beginnt, da die Terminaloperation collect (...) aufgerufen wird. Der Stream wird Zeile für Zeile träge gefüllt. Es ist nicht erforderlich, die gesamte Datei vor der Verarbeitung im Speicher zu lesen (z. B. Filtern und Zuordnen).
Andrei N
vor der Auswahl nicht leerer Linien trimmen?
Thorbjørn Ravn Andersen
50

Dieser Code normalisiert Zeilenumbrüche, die möglicherweise das sind, was Sie wirklich tun möchten oder nicht.

Hier ist eine Alternative, die das nicht tut und die (IMO) einfacher zu verstehen ist als der NIO-Code (obwohl er immer noch verwendet wird java.nio.charset.Charset):

public static String readFile(String file, String csName)
            throws IOException {
    Charset cs = Charset.forName(csName);
    return readFile(file, cs);
}

public static String readFile(String file, Charset cs)
            throws IOException {
    // No real need to close the BufferedReader/InputStreamReader
    // as they're only wrapping the stream
    FileInputStream stream = new FileInputStream(file);
    try {
        Reader reader = new BufferedReader(new InputStreamReader(stream, cs));
        StringBuilder builder = new StringBuilder();
        char[] buffer = new char[8192];
        int read;
        while ((read = reader.read(buffer, 0, buffer.length)) > 0) {
            builder.append(buffer, 0, read);
        }
        return builder.toString();
    } finally {
        // Potential issue here: if this throws an IOException,
        // it will mask any others. Normally I'd use a utility
        // method which would log exceptions and swallow them
        stream.close();
    }        
}
Jon Skeet
quelle
1
Verzeihen Sie mir, dass ich einen so alten Kommentar wiederbelebt habe, aber wollten Sie ein String-Objekt namens "file" übergeben, oder sollte das stattdessen ein File-Objekt sein?
Bryan Larson
28

Sammelte alle möglichen Möglichkeiten, um die Datei als Zeichenfolge von der Festplatte oder vom Netzwerk zu lesen.

  • Guave: Google benutzt Klassen Resources,Files

    static Charset charset = com.google.common.base.Charsets.UTF_8;
    public static String guava_ServerFile( URL url ) throws IOException {
        return Resources.toString( url, charset );
    }
    public static String guava_DiskFile( File file ) throws IOException {
        return Files.toString( file, charset );
    }

  • APACHE - GEMEINSAME E / A mit den Klassen IOUtils, FileUtils

    static Charset encoding = org.apache.commons.io.Charsets.UTF_8;
    public static String commons_IOUtils( URL url ) throws IOException {
        java.io.InputStream in = url.openStream();
        try {
            return IOUtils.toString( in, encoding );
        } finally {
            IOUtils.closeQuietly(in);
        }
    }
    public static String commons_FileUtils( File file ) throws IOException {
        return FileUtils.readFileToString( file, encoding );
        /*List<String> lines = FileUtils.readLines( fileName, encoding );
        return lines.stream().collect( Collectors.joining("\n") );*/
    }

  • Java 8 BufferReader mit Stream API

    public static String streamURL_Buffer( URL url ) throws IOException {
        java.io.InputStream source = url.openStream();
        BufferedReader reader = new BufferedReader( new InputStreamReader( source ) );
        //List<String> lines = reader.lines().collect( Collectors.toList() );
        return reader.lines().collect( Collectors.joining( System.lineSeparator() ) );
    }
    public static String streamFile_Buffer( File file ) throws IOException {
        BufferedReader reader = new BufferedReader( new FileReader( file ) );
        return reader.lines().collect(Collectors.joining(System.lineSeparator()));
    }

  • Scannerklasse mit Regex \A. Dies entspricht dem Beginn der Eingabe.

    static String charsetName = java.nio.charset.StandardCharsets.UTF_8.toString();
    public static String streamURL_Scanner( URL url ) throws IOException {
        java.io.InputStream source = url.openStream();
        Scanner scanner = new Scanner(source, charsetName).useDelimiter("\\A");
        return scanner.hasNext() ? scanner.next() : "";
    }
    public static String streamFile_Scanner( File file ) throws IOException {
        Scanner scanner = new Scanner(file, charsetName).useDelimiter("\\A");
        return scanner.hasNext() ? scanner.next() : "";
    }

  • Java 7 ( java.nio.file.Files.readAllBytes)

    public static String getDiskFile_Java7( File file ) throws IOException {
        byte[] readAllBytes = java.nio.file.Files.readAllBytes(Paths.get( file.getAbsolutePath() ));
        return new String( readAllBytes );
    }

  • BufferedReadermit InputStreamReader.

    public static String getDiskFile_Lines( File file ) throws IOException {
        StringBuffer text = new StringBuffer();
        FileInputStream fileStream = new FileInputStream( file );
        BufferedReader br = new BufferedReader( new InputStreamReader( fileStream ) );
        for ( String line; (line = br.readLine()) != null; )
            text.append( line + System.lineSeparator() );
        return text.toString();
    }

Beispiel mit Hauptmethode für den Zugriff auf die oben genannten Methoden.

public static void main(String[] args) throws IOException {
    String fileName = "E:/parametarisation.csv";
    File file = new File( fileName );

    String fileStream = commons_FileUtils( file );
            // guava_DiskFile( file );
            // streamFile_Buffer( file );
            // getDiskFile_Java7( file );
            // getDiskFile_Lines( file );
    System.out.println( " File Over Disk : \n"+ fileStream );


    try {
        String src = "https://code.jquery.com/jquery-3.2.1.js";
        URL url = new URL( src );

        String urlStream = commons_IOUtils( url );
                // guava_ServerFile( url );
                // streamURL_Scanner( url );
                // streamURL_Buffer( url );
        System.out.println( " File Over Network : \n"+ urlStream );
    } catch (MalformedURLException e) {
        e.printStackTrace();
    }
}

@sehen

Yash
quelle
26

Wenn es sich um eine Textdatei handelt, warum nicht Apache Commons-Io verwenden ?

Es hat die folgende Methode

public static String readFileToString(File file) throws IOException

Wenn Sie die Zeilen als Liste verwenden möchten, verwenden Sie

public static List<String> readLines(File file) throws IOException
Zuhause in der Zeit
quelle
25

Seit JDK 11:

String file = ...
Path path = Paths.get(file);
String content = Files.readString(path);
// Or readString(path, someCharset), if you need a Charset different from UTF-8
leventov
quelle
Warum, oh warum, 2018 neue Methoden einführen, die auf dem Standardzeichensatz basieren?
Bryan
2
@mryan Diese Methode basiert nicht auf dem Standardsystem-Zeichensatz. Der Standardwert ist UTF-8, das ist in Ordnung.
Leventov
@leventov du hast recht! Files.readAllLines auch! das macht die Dateien API nicht sehr konsistent mit älteren Methoden, aber es ist zum Besseren :)
mryan
17

Eine Datei als Binärdatei lesen und am Ende konvertieren

public static String readFileAsString(String filePath) throws IOException {
    DataInputStream dis = new DataInputStream(new FileInputStream(filePath));
    try {
        long len = new File(filePath).length();
        if (len > Integer.MAX_VALUE) throw new IOException("File "+filePath+" too large, was "+len+" bytes.");
        byte[] bytes = new byte[(int) len];
        dis.readFully(bytes);
        return new String(bytes, "UTF-8");
    } finally {
        dis.close();
    }
}
Peter Lawrey
quelle
16

Mit Java 7 ist dies meine bevorzugte Option zum Lesen einer UTF-8-Datei:

String content = new String(Files.readAllBytes(Paths.get(filename)), "UTF-8");

Seit Java 7 verfügt das JDK über die neue java.nio.fileAPI, die viele Verknüpfungen bietet, sodass Bibliotheken von Drittanbietern für einfache Dateivorgänge nicht immer erforderlich sind.

Moritz Petersen
quelle
15

Java versucht, in allem, was es tut, extrem allgemein und flexibel zu sein. Infolgedessen ist etwas, das in einer Skriptsprache relativ einfach ist (Ihr Code würde open(file).read()in Python durch " " ersetzt), viel komplizierter. Es scheint keinen kürzeren Weg zu geben, als eine externe Bibliothek zu verwenden (wie Willi aus Rohr erwähnt hat). Deine Optionen:

  • Verwenden Sie eine externe Bibliothek.
  • Kopieren Sie diesen Code in alle Ihre Projekte.
  • Erstellen Sie Ihre eigene Mini-Bibliothek, die Funktionen enthält, die Sie häufig verwenden.

Ihre beste Wette ist wahrscheinlich die 2., da sie die geringsten Abhängigkeiten aufweist.

Claudiu
quelle
4
Ja. Dadurch erhält die "Hochsprache" eine andere Bedeutung. Java ist hoch im Vergleich zu C, aber niedrig im Vergleich zu Python oder Ruby
OscarRyz
3
Stimmen Sie zu, dass Java lange auf Abstraktionen auf hoher Ebene, aber kurz auf Convenience-Methoden ist
Dónal
3
Es stimmt, Java hat eine verrückte Anzahl von Möglichkeiten, mit Dateien umzugehen, und viele davon scheinen kompliziert zu sein. Aber das ist ziemlich nah an dem, was wir in höheren Sprachen haben:byte[] bytes = Files.readAllBytes(someFile.toPath());
Thorn
11

Verwenden von JDK 8 oder höher:

Keine externen Bibliotheken verwendet

Sie können ein neues String-Objekt aus dem Dateiinhalt erstellen (Verwenden von Klassen aus dem java.nio.filePaket):

public String readStringFromFile(String filePath) throws IOException {
    String fileContent = new String(Files.readAllBytes(Paths.get(filePath)));
    return fileContent;
}
Saikat
quelle
Duplikat der Antwort von Moritz Petersen, der geschrieben hat: String content = new String (Files.readAllBytes (Paths.get (Dateiname)), "UTF-8");
Jean-Christophe Blanchard
8

Es gibt eine Variation desselben Themas, bei der anstelle einer while-Schleife eine for-Schleife verwendet wird, um den Umfang der Zeilenvariablen einzuschränken. Ob es "besser" ist, ist eine Frage des persönlichen Geschmacks.

for(String line = reader.readLine(); line != null; line = reader.readLine()) {
    stringBuilder.append(line);
    stringBuilder.append(ls);
}
Dan Dyer
quelle
3
Dadurch werden die Zeilenumbrüche in die Standardauswahl für Zeilenumbrüche geändert. Dies kann wünschenswert oder unbeabsichtigt sein.
Peter Lawrey
Die Bearbeitung wurde auf diese Antwort zurückgesetzt, da der Umfang der lineVariablen eingeschränkt werden sollte. Die Bearbeitung hat es zweimal deklariert, was ein Kompilierungsfehler wäre.
Dan Dyer
7

Wenn Sie keinen Zugriff auf die FilesKlasse haben, können Sie eine native Lösung verwenden.

static String readFile(File file, String charset)
        throws IOException
{
    FileInputStream fileInputStream = new FileInputStream(file);
    byte[] buffer = new byte[fileInputStream.available()];
    int length = fileInputStream.read(buffer);
    fileInputStream.close();
    return new String(buffer, 0, length, charset);
}
Ilya Gazman
quelle
Beispiel Zeichensatz zum Aufrufen?
Thufir
4

Eine flexible Lösung mit IOUtils von Apache commons-io in Kombination mit StringWriter :

Reader input = new FileReader();
StringWriter output = new StringWriter();
try {
  IOUtils.copy(input, output);
} finally {
  input.close();
}
String fileContents = output.toString();

Es funktioniert mit jedem Reader oder Eingabestream (nicht nur mit Dateien), beispielsweise beim Lesen von einer URL.

wau
quelle
3

Beachten Sie, dass bei Verwendung fileInputStream.available()der zurückgegebenen Ganzzahl nicht die tatsächliche Dateigröße angegeben werden muss, sondern die geschätzte Anzahl von Bytes, die das System aus dem Stream lesen kann, ohne die E / A zu blockieren. Ein sicherer und einfacher Weg könnte so aussehen

public String readStringFromInputStream(FileInputStream fileInputStream) {
    StringBuffer stringBuffer = new StringBuffer();
    try {
        byte[] buffer;
        while (fileInputStream.available() > 0) {
            buffer = new byte[fileInputStream.available()];
            fileInputStream.read(buffer);
            stringBuffer.append(new String(buffer, "ISO-8859-1"));
        }
    } catch (FileNotFoundException e) {
    } catch (IOException e) { }
    return stringBuffer.toString();
}

Es sollte berücksichtigt werden, dass dieser Ansatz nicht für Mehrbyte-Zeichencodierungen wie UTF-8 geeignet ist.

Henry
quelle
1
Dieser Code kann zu unvorhersehbaren Ergebnissen führen. Gemäß der Dokumentation der available()Methode gibt es keine Garantie dafür, dass das Dateiende erreicht ist, falls die Methode 0 zurückgibt. In diesem Fall erhalten Sie möglicherweise eine unvollständige Datei. Was noch schlimmer ist, die Anzahl der tatsächlich gelesenen Bytes kann kleiner sein als der von zurückgegebene Wert available(). In diesem Fall erhalten Sie eine beschädigte Ausgabe.
Wau
3

Dieser verwendet die Methode RandomAccessFile.readFully, sie scheint ab JDK 1.0 verfügbar zu sein!

public static String readFileContent(String filename, Charset charset) throws IOException {
    RandomAccessFile raf = null;
    try {
        raf = new RandomAccessFile(filename, "r");
        byte[] buffer = new byte[(int)raf.length()];
        raf.readFully(buffer);
        return new String(buffer, charset);
    } finally {
        closeStream(raf);
    }
} 


private static void closeStream(Closeable c) {
    if (c != null) {
        try {
            c.close();
        } catch (IOException ex) {
            // do nothing
        }
    }
}
Barjak
quelle
3

Sie können Scanner- und Dateiklasse ausprobieren, eine Lösung mit wenigen Zeilen

 try
{
  String content = new Scanner(new File("file.txt")).useDelimiter("\\Z").next();
  System.out.println(content);
}
catch(FileNotFoundException e)
{
  System.out.println("not found!");
}
Jamesjara
quelle
3

Benutzer java.nio.Files, um alle Zeilen der Datei zu lesen.

public String readFile() throws IOException {
        File fileToRead = new File("file path");
        List<String> fileLines = Files.readAllLines(fileToRead.toPath());
        return StringUtils.join(fileLines, StringUtils.EMPTY);
}
Nitin Vavdiya
quelle
3
public static String slurp (final File file)
throws IOException {
    StringBuilder result = new StringBuilder();

    BufferedReader reader = new BufferedReader(new FileReader(file));

    try {
        char[] buf = new char[1024];

        int r = 0;

        while ((r = reader.read(buf)) != -1) {
            result.append(buf, 0, r);
        }
    }
    finally {
        reader.close();
    }

    return result.toString();
}
Scott S. McCoy
quelle
Ich denke, dies hat die Unannehmlichkeiten bei der Verwendung der Standardcodierung der Plattform. +1 sowieso :)
OscarRyz
7
Mir scheint, dass der finally-Block keine im try-Block definierten Variablen kennt. javac 1.6.0_21 löst den Fehler aus cannot find symbol.
Ceving
Haben Sie sogar Ihren eigenen Code ausprobiert? Sie haben den Reader im try / catch-Block definiert, sodass er im finally-Block nicht verfügbar ist.
Mauron85
2

Ich kann noch keine anderen Einträge kommentieren, also lasse ich es einfach hier.

Eine der besten Antworten hier ( https://stackoverflow.com/a/326448/1521167 ):

private String readFile(String pathname) throws IOException {

File file = new File(pathname);
StringBuilder fileContents = new StringBuilder((int)file.length());
Scanner scanner = new Scanner(file);
String lineSeparator = System.getProperty("line.separator");

try {
    while(scanner.hasNextLine()) {        
        fileContents.append(scanner.nextLine() + lineSeparator);
    }
    return fileContents.toString();
} finally {
    scanner.close();
}
}

hat noch einen Fehler. Es wird immer ein neues Zeilenzeichen am Ende des Strings eingefügt, was einige seltsame Fehler verursachen kann. Mein Vorschlag ist, es zu ändern in:

    private String readFile(String pathname) throws IOException {
    File file = new File(pathname);
    StringBuilder fileContents = new StringBuilder((int) file.length());
    Scanner scanner = new Scanner(new BufferedReader(new FileReader(file)));
    String lineSeparator = System.getProperty("line.separator");

    try {
        if (scanner.hasNextLine()) {
            fileContents.append(scanner.nextLine());
        }
        while (scanner.hasNextLine()) {
            fileContents.append(lineSeparator + scanner.nextLine());
        }
        return fileContents.toString();
    } finally {
        scanner.close();
    }
}
Ajk
quelle
Im ersten Fall fügen Sie am Ende möglicherweise eine zusätzliche Zeile hinzu. Im zweiten Fall lassen Sie möglicherweise einen weg. Also sind beide gleichermaßen falsch. Siehe diesen Artikel
Patrick Parker
2

Nach Strg + F'ing nach Scanner denke ich, dass die Scannerlösung auch aufgelistet werden sollte. Auf die am einfachsten zu lesende Weise geht es so:

public String fileToString(File file, Charset charset) {
  Scanner fileReader = new Scanner(file, charset);
  fileReader.useDelimiter("\\Z"); // \Z means EOF.
  String out = fileReader.next();
  fileReader.close();
  return out;
}

Wenn Sie Java 7 oder höher verwenden (und dies sollten Sie wirklich tun), sollten Sie Try-with-Resources verwenden, um das Lesen des Codes zu vereinfachen. Kein punktnahes Zeug mehr, das alles verschmutzt. Aber das ist meistens eine stilistische Entscheidung.

Ich poste dies hauptsächlich aus Gründen der Vervollständigung, denn wenn Sie dies häufig tun müssen, sollte es Dinge in java.nio.file.Files geben , die die Arbeit besser machen sollten.

Mein Vorschlag wäre, Files # readAllBytes (Path) zu verwenden, um alle Bytes zu erfassen und sie einem neuen String (byte [] Charset) zuzuführen. , um einen String daraus zu erhalten, dem Sie vertrauen können. Charsets werden für Sie während Ihres Lebens gemein sein, also hüten Sie sich jetzt vor diesem Zeug.

Andere haben Code und so gegeben, und ich möchte ihren Ruhm nicht stehlen. ;)

Haakon Løtveit
quelle
2

Bei Verwendung dieser Bibliothek handelt es sich um eine Zeile:

String data = IO.from(new File("data.txt")).toString();
Satnam
quelle
1
wenn die Zeilen in der Bibliothek nicht gezählt werden.
Ari
2

Auch wenn sich Ihre Datei in einem Glas befindet, können Sie Folgendes verwenden:

public String fromFileInJar(String path) {
    try ( Scanner scanner 
            = new Scanner(getClass().getResourceAsStream(path))) {
        return scanner.useDelimiter("\\A").next();
    }
}

Der Pfad sollte / zum Beispiel mit beginnen, wenn Ihr Glas ist

my.jar/com/some/thing/a.txt

Dann möchten Sie es so aufrufen:

String myTxt = fromFileInJar("/com/com/thing/a.txt");
OscarRyz
quelle
2

In einer Zeile (Java 8), vorausgesetzt, Sie haben einen Reader:

String sMessage = String.join("\n", reader.lines().collect(Collectors.toList()));
Malcolm Boekhoff
quelle
2

Basierend auf der Antwort von @ erickson können Sie Folgendes verwenden:

public String readAll(String fileName) throws IOException {
    List<String> lines = Files.readAllLines(new File(fileName).toPath());
    return String.join("\n", lines.toArray(new String[lines.size()]));
}
Muskovets
quelle