Jak utworzyć ciąg Java z zawartości pliku?


1513

Od jakiegoś czasu używam poniższego idiomu. I wydaje się być najbardziej rozpowszechniony, przynajmniej w witrynach, które odwiedziłem.

Czy jest lepszy / inny sposób wczytywania pliku do łańcucha w Javie?

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

7
Czy ktoś może mi wyjaśnić w bardzo prosty sposób, co jest z NIO? Za każdym razem, gdy o tym czytam, gubię się w n-tej wzmiance o kanale :(
OscarRyz

7
pamiętajcie, że nie ma gwarancji, że separator linii w pliku nie jest konieczny tak samo jak systemowy separator linii.
Henrik Paul,

138
Czy możesz w końcu wstawić odpowiednią próbę, która zamyka czytnik? Ktoś może faktycznie skorzystać z tego przykładu i wprowadzić błąd w swoim kodzie.
Hans-Peter Störr,

6
Powyższy kod zawiera błąd polegający na dodaniu dodatkowego znaku nowej linii w ostatnim wierszu. Powinno to wyglądać podobnie do następującego, jeśli (line = reader.readLine ())! = Null) {stringBuilder.append (line); } while (line = reader.readLine ())! = null) {stringBuilder.append (ls); stringBuilder.append (linia); }
Głęboki

27
Java 7 przedstawia się byte[] Files.readAllBytes(file);tym, którzy sugerują skaner „jednowierszowy”: czy nie musisz go zamykać?
Val

Odpowiedzi:


1533

Przeczytaj cały tekst z pliku

Java 11 dodała metodę readString () do odczytu małych plików jako String, zachowując terminatory linii:

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

Dla wersji między Java 7 a 11, oto kompaktowy, solidny idiom zawarty w metodzie narzędziowej:

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

Czytaj wiersze tekstu z pliku

Java 7 dodała wygodną metodę odczytu pliku jako wiersza tekstu reprezentowanego jako List<String>. To podejście jest „stratne”, ponieważ separatory linii są usuwane z końca każdej linii.

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

Java 8 dodała Files.lines()metodę tworzenia Stream<String>. Ponownie, ta metoda jest stratna, ponieważ separatory linii są usuwane. Jeśli IOExceptionpodczas odczytu pliku zostanie napotkany, jest on zawijany w UncheckedIOException, ponieważ Streamnie akceptuje lambd, które zgłaszają sprawdzone wyjątki.

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

To Streamwymaga close()połączenia; jest to słabo udokumentowane w interfejsie API i podejrzewam, że wiele osób nawet nie zauważa, że Streammaclose() metodę. Pamiętaj, aby użyć bloku ARM, jak pokazano.

Jeśli pracujesz ze źródłem innym niż plik, możesz zamiast tego użyć lines()metodyBufferedReader

Wykorzystanie pamięci

Pierwsza metoda, która zachowuje podział wiersza, może tymczasowo wymagać pamięci kilkakrotnie większej niż rozmiar pliku, ponieważ przez krótki czas surowa zawartość pliku (tablica bajtów) i zdekodowane znaki (z których każdy ma 16 bitów, nawet jeśli jest zakodowany 8 plików w pliku) znajdują się jednocześnie w pamięci. Najbezpieczniej jest zastosować do plików, o których wiadomo, że są małe w stosunku do dostępnej pamięci.

Druga metoda, czytanie linii, jest zwykle bardziej wydajna pod względem pamięci, ponieważ bufor bajtów wejściowych do dekodowania nie musi zawierać całego pliku. Jednak nadal nie nadaje się do plików, które są bardzo duże w stosunku do dostępnej pamięci.

Do odczytu dużych plików potrzebujesz innego projektu dla swojego programu, który odczytuje fragment tekstu ze strumienia, przetwarza go, a następnie przechodzi do następnego, ponownie wykorzystując ten sam blok pamięci o stałej wielkości. Tutaj „duży” zależy od specyfikacji komputera. Obecnie ten próg może wynosić wiele gigabajtów pamięci RAM. Trzecia metoda, przy użyciu a, Stream<String>jest jednym ze sposobów, aby to zrobić, jeśli twoje wejściowe „rekordy” są przypadkami pojedynczych linii. (Zastosowanie readLine()metody BufferedReaderjest proceduralnym odpowiednikiem tego podejścia).

Kodowanie znaków

Jednej rzeczy, której brakuje w próbce w oryginalnym poście, jest kodowanie znaków. Istnieją pewne szczególne przypadki, w których domyślna platforma jest tym, czego chcesz, ale są one rzadkie i powinieneś być w stanie uzasadnić swój wybór.

StandardCharsetsKlasa zdefiniowanie pewnych stałych dla kodowania wymagane od wszystkich środowisk wykonawczych Java:

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

Domyślna platforma jest dostępna wCharset samej klasie :

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

Uwaga: ta odpowiedź w dużej mierze zastępuje moją wersję Java 6. Narzędzie Java 7 bezpiecznie upraszcza kod, a stara odpowiedź, która używała odwzorowanego bufora bajtów, uniemożliwiała usunięcie odczytanego pliku, dopóki zmapowany bufor nie został wyrzucony. Możesz wyświetlić starą wersję za pomocą linku „edytowanego” w tej odpowiedzi.


3
Technicznie rzecz biorąc, jest to O (n) w czasie i przestrzeni. Jakościowo, ze względu na wymaganie niezmienności Ciągów, jest to dość trudne w pamięci; tymczasowo w pamięci znajdują się dwie kopie danych char oraz miejsce na zakodowane bajty. Zakładając pewne kodowanie jednobajtowe, będzie (tymczasowo) wymagać 5 bajtów pamięci na każdy znak w pliku. Ponieważ pytanie dotyczy konkretnie ciągu znaków, właśnie to pokazuję, ale jeśli możesz pracować z CharBufferem zwróconym przez „dekodowanie”, zapotrzebowanie na pamięć jest znacznie mniejsze. Jeśli chodzi o czas, nie sądzę, że znajdziesz coś szybszego w podstawowych bibliotekach Java.
erickson,

5
Możliwa literówka? NIO ma klasę Charset (nie CharSet) o nazwie java.nio.charset.Charset. Czy taki powinien być CharSet?
Jonathan Wright,

31
Uwaga: po zapoznaniu się z tym kodem odkryłem, że nie można niezawodnie usunąć pliku zaraz po przeczytaniu go za pomocą tej metody, co w niektórych przypadkach może nie być problemem, ale nie moim. Czy może to mieć związek z tym problemem: bugs.sun.com/bugdatabase/view_bug.do?bug_id=4715154 ? W końcu zdecydowałem się na propozycję Jona Skeeta, który nie cierpi na ten błąd. Tak czy inaczej, chciałem tylko przekazać informacje innym osobom, na wszelki wypadek ...
Sébastien Nussbaumer

5
@ Sébastien Nussbaumer: Wpadłem również na ten problem. Zadziwiające, że błąd został oznaczony jako „Nie naprawi się”. Zasadniczo oznacza FileChannel#mapto, że zasadniczo nie nadaje się do użytku.
Joonas Pulakka

4
@ Sébastien Nussbaumer: Błąd został usunięty z bazy danych błędów Oracle / Sun: „Ten błąd jest niedostępny”. Google zbuforował witrynę pod adresem webcache.googleusercontent.com/search?q=cache:bugs.sun.com/…
Bobndrew

350

Jeśli chcesz korzystać z zewnętrznej biblioteki, sprawdź Apache Commons IO (200 KB JAR). Zawiera org.apache.commons.io.FileUtils.readFileToString()metodę, która pozwala wczytać całość Filedo Stringjednego wiersza kodu.

Przykład:

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

Nie znajduję tej metody w podanym adresie URL.
OscarRyz

2
Jest w klasie org.apache.commons.io.FileUtils
Cyrille Ka

2
Korzystam też z FileUtils, ale zastanawiam się, co jest lepsze pomiędzy korzystaniem z FileUtils lub z zaakceptowanej odpowiedzi nio?
Guillaume,

4
@Guillaume: Największe pytanie dotyczy tego, czy czujesz się komfortowo, polegając na bibliotece innej firmy. Jeśli masz w projekcie Commons IO lub Guava , użyj tego (tylko dla uproszczenia kodu; w przeciwnym razie prawdopodobnie nie będzie zauważalnej różnicy).
Jonik

183

Bardzo oszczędne rozwiązanie oparte na Scanner:

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

Lub, jeśli chcesz ustawić zestaw znaków:

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

Lub z blokiem try-with-resources , który woła scanner.close()cię:

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

Pamiętaj, że Scannerkonstruktor może wyrzucić IOException. I nie zapomnij zaimportować java.ioijava.util .

Źródło: blog Pat Niemeyer


4
\\ A działa, ponieważ nie ma „innego początku pliku”, więc w rzeczywistości czytasz ostatni token ... który jest również pierwszym. Nigdy nie próbowałem z \\ Z. Pamiętaj też, że możesz odczytać wszystko, co jest czytelne, takie jak Pliki, Strumienie wejściowe, kanały ... Czasami używam tego kodu do odczytu z okna wyświetlania zaćmienia, gdy nie jestem pewien, czy czytam jeden plik czy inny ... .yes, classpath mnie myli.
Pablo Grisafi

1
Jako plakat mogę powiedzieć, że naprawdę nie wiem, czy i kiedy plik jest właściwie zamknięty ... Nigdy nie piszę tego w kodzie produkcyjnym, używam go tylko do testów lub debugowania.
Pablo Grisafi

2
Wydaje mi się, że ma limit 1024 znaków
kapryśny

20
Skaner implementuje Closeable (wywołuje zamknięcie na źródle) - więc choć elegancki, nie powinien być tak naprawdę jednowarstwowy. Domyślny rozmiar bufora to 1024, ale skaner zwiększy rozmiar w razie potrzeby (patrz Scanner # makeSpace ())
kamera douszna

8
Ten kończy się niepowodzeniem dla pustych plików z java.util.NoSuchElementException.
SpaceTrucker

116
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);

od wersji Java 7 możesz to zrobić w ten sposób.


Należy to zaakceptować jako odpowiedź - pojedyncza linia, brak zewnętrznych bibliotek.
Wiśnia

To dodało znak nowej linii na końcu, nawet jeśli nie był obecny w pliku
Stefan Haberl

79

Jeśli szukasz alternatywy, która nie wymaga biblioteki innej firmy (np Commons I / O ), możesz użyć klasy Scanner :

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

2
Myślę, że to najlepszy sposób. Sprawdź java.sun.com/docs/books/tutorial/essential/io/scanning.html
Tarski

3
Konstruktor skanera, który akceptuje łańcuch, nie traktuje łańcucha jako nazwy pliku do odczytu, ale jako skanowany tekst. Cały czas popełniam ten błąd. : - /
Alan Moore

@Alan, dobry połów. Lekko zredagowałem odpowiedź Dona, aby to naprawić (mam nadzieję).
Jonik

3
fileContents.append (scanner.nextLine ()). append (lineSeparator);
zakaz geoinżynierii

1
Zmień instrukcję inicjalizacji na Scanner scanner = new Scanner((Readable) new BufferedReader(new FileReader(file)));. W przeciwnym razie możesz przechwycić tylko część pliku.
Wei Yang,

71

Guawa ma metodę podobną do tej z Commons IOUtils, o której wspominał Willi aus Rohr:

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

// ...

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

Edycja przez PiggyPiglet
Files#toString jest przestarzała i należy ją usunąć Octobor 2019. Zamiast tego użyj Files.asCharSource(new File(path), StandardCharsets.UTF_8).read();

EDYCJA Oscara Reyesa

Oto (uproszczony) podstawowy kod cytowanej biblioteki:

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

Edycja (autor: Jonik): Powyższe nie pasuje do kodu źródłowego najnowszych wersji Guava. Bieżące źródło znajduje się w klasach Pliki , CharStreams , ByteSource i CharSource w pakiecie com.google.common.io .


Ten kod ma rzutowanie z długiego na int, co może wywołać szalone zachowanie przy dużych plikach. Ma dodatkowe spacje i gdzie zamykasz strumień wejściowy?
Mohamed Taher Alrefaie

@MTA: Strumień jest zamknięty, zwróć uwagę na użycie Closerw CharSource . Kod w odpowiedzi nie jest faktycznym, aktualnym źródłem guawy.
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 "";
    }

6
Lub jeszcze new String(Files.readAllBytes(FileSystems.getDefault().getPath( filename)));

12
lub new String(Files.readAllBytes(Paths.get(filename)));:-)
assafmo

1
Dobrze grał, i zapisać następny facet Googling, Pathswidocznie 1.7+ jak jest FileSystems. (Cholera!)
ruffin,

4
Szkoda, że ​​ta odpowiedź nie ma więcej głosów. Szukałem najszybszego i najprostszego sposobu na umieszczenie pliku tekstowego w łańcuchu. To jest to, a gdybym nie przewijał w dół, w dół iw dół, to bym tego nie zauważył. PO powinien rozważyć zaakceptowanie tej odpowiedzi, aby przenieść ją na szczyt.
Thorn

@Thorn Ta odpowiedź ma straszliwą obsługę błędów. Nie używaj tej metody w kodzie produkcyjnym lub lepiej: nigdy.
xehpuk

51

Jeśli potrzebujesz przetwarzania łańcucha (przetwarzanie równoległe), Java 8 ma świetne API Stream.

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

Więcej przykładów dostępnych jest w przykładach JDK, sample/lambda/BulkDataOperationsktóre można pobrać ze strony pobierania Oracle Java SE 8

Kolejny przykład liniowej

String out = String.join("\n", Files.readAllLines(Paths.get("file.txt")));

Czy .parallel () ma miejsce po przeczytaniu linii czy wcześniej?
Istvan

Prawdziwa praca rozpoczyna się od momentu wywołania operacji zbierania (...) terminala. Strumień jest leniwie zapełniany linia po linii. Nie ma potrzeby czytania całego pliku w pamięci przed przetwarzaniem (np. Filtrowanie i mapowanie).
Andrei N

przyciąć przed wybraniem niepustych linii?
Thorbjørn Ravn Andersen

50

Ten kod normalizuje podział wiersza, który może, ale nie musi być tym, co naprawdę chcesz zrobić.

Oto alternatywa, która tego nie robi i która jest (IMO) prostsza do zrozumienia niż kod NIO (chociaż nadal używa 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();
    }        
}

1
Wybacz, że ożywiłem ten stary komentarz, ale czy chciałeś przekazać obiekt String o nazwie „file”, czy zamiast tego powinien to być obiekt File?
Bryan Larson

28

Zebrano wszystkie możliwe sposoby odczytu pliku jako ciągu z dysku lub sieci.

  • Guawa: Google za pomocą klas 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 - WSPÓLNE IO za pomocą klas 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 przy użyciu 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()));
    }

  • Klasa skanera z wyrażeniem regularnym \A. który pasuje do początku danych wejściowych.

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

  • BufferedReaderza pomocą 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();
    }

Przykład z główną metodą dostępu do powyższych metod.

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

@widzieć


26

Jeśli jest to plik tekstowy, dlaczego nie użyć apache commons-io ?

Ma następującą metodę

public static String readFileToString(File file) throws IOException

Jeśli chcesz linie jako listę, użyj

public static List<String> readLines(File file) throws IOException

25

Od 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

Dlaczego och, dlaczego wprowadzamy nowe metody, które opierają się na domyślnym zestawie znaków w 2018 roku?
mryan

2
@ mryan ta metoda nie opiera się na domyślnym zestawie znaków systemowych. Domyślnie jest to UTF-8, to dobrze.
leventov

@leventov masz rację! podobnie jak Files.readAllLines! powoduje to, że interfejs API plików nie jest zbyt spójny ze starszymi metodami, ale jest na lepsze :)
mryan

17

Aby odczytać plik jako plik binarny i przekonwertować na końcu

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

16

W Javie 7 jest to moja preferowana opcja odczytu pliku UTF-8:

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

Od wersji Java 7 JDK ma nowy java.nio.fileinterfejs API, który zapewnia wiele skrótów, więc biblioteki innych firm nie zawsze są wymagane do prostych operacji na plikach.


15

Java stara się być bardzo ogólna i elastyczna we wszystkim, co robi. W rezultacie coś, co jest stosunkowo proste w języku skryptowym (twój kod zostałby zastąpiony przez „ open(file).read()” w pythonie) jest o wiele bardziej skomplikowane. Wydaje się, że nie ma krótszego sposobu na zrobienie tego, z wyjątkiem korzystania z zewnętrznej biblioteki (jak wspomniano Willi aus Rohr ). Twoje opcje:

  • Użyj biblioteki zewnętrznej.
  • Skopiuj ten kod do wszystkich swoich projektów.
  • Utwórz własną minibibliotekę, która zawiera często używane funkcje.

Twój najlepszy zakład to prawdopodobnie drugi, ponieważ ma najmniej zależności.


4
Tak Sprawia, że ​​język „wysokiego” poziomu ma inne znaczenie. Java ma wysoki poziom w porównaniu z C, ale niski w porównaniu z Pythonem lub Ruby
OscarRyz

3
Zgadzam się, że Java od dawna zajmuje się abstrakcjami na wysokim poziomie, ale mało metod wygodnych
Dónal

3
To prawda, że ​​Java ma szaloną liczbę sposobów radzenia sobie z plikami i wiele z nich wydaje się skomplikowanych. Ale jest to dość zbliżone do tego, co mamy w językach wyższego poziomu:byte[] bytes = Files.readAllBytes(someFile.toPath());
Thorn

11

Przy użyciu JDK 8 lub nowszej:

brak bibliotek zewnętrznych

Możesz utworzyć nowy obiekt String z zawartości pliku (Korzystanie z klas z java.nio.filepakietu):

public String readStringFromFile(String filePath) throws IOException {
    String fileContent = new String(Files.readAllBytes(Paths.get(filePath)));
    return fileContent;
}

Duplikat odpowiedzi Moritza Petersena, który napisał: String content = new String (Files.readAllBytes (Paths.get (nazwa pliku)), „UTF-8”);
Jean-Christophe Blanchard

8

Istnieje wariant tego samego motywu, który używa pętli for zamiast pętli while, aby ograniczyć zakres zmiennej liniowej. To, czy jest „lepiej”, zależy od osobistego gustu.

for(String line = reader.readLine(); line != null; line = reader.readLine()) {
    stringBuilder.append(line);
    stringBuilder.append(ls);
}

3
Spowoduje to zmianę nowych linii na domyślny wybór nowej linii. Może to być pożądane lub niezamierzone.
Peter Lawrey,

Wycofałem edycję do tej odpowiedzi, ponieważ chodziło o zawężenie zakresu linezmiennej. Edycja zadeklarowała to dwukrotnie, co byłoby błędem kompilacji.
Dan Dyer

7

Jeśli nie masz dostępu do Filesklasy, możesz użyć rozwiązania natywnego.

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

przykładowy zestaw znaków do wywołania?
Thufir

4

Elastyczne rozwiązanie wykorzystujące IOUtils z Apache commons-io w połączeniu z StringWriter :

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

Działa z dowolnym czytnikiem lub strumieniem wejściowym (nie tylko z plikami), na przykład podczas czytania z adresu URL.


3

Należy pamiętać, że użycie fileInputStream.available()zwracanej liczby całkowitej nie musi reprezentować rzeczywistego rozmiaru pliku, ale raczej odgadniętą liczbę bajtów, którą system powinien móc odczytać ze strumienia bez blokowania IO. Bezpieczny i prosty sposób może wyglądać tak

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

Należy wziąć pod uwagę, że to podejście nie jest odpowiednie dla kodowania znaków wielobajtowych, takich jak UTF-8.


1
Ten kod może dawać nieprzewidywalne wyniki. Według dokumentacji zavailable() metodzie, nie ma gwarancji, że koniec pliku zostanie osiągnięty w przypadku, metoda zwraca 0. W takim wypadku może skończyć się z niekompletnego pliku. Co gorsza, liczba faktycznie odczytanych bajtów może być mniejsza niż wartość zwracana przez available(), w którym to przypadku otrzymujesz uszkodzone dane wyjściowe.
wau

3

Ten używa metody RandomAccessFile.readFully, wydaje się być dostępny z JDK 1.0!

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
        }
    }
}

3

Możesz wypróbować skaner i klasę plików, rozwiązanie kilku linii

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

3

Użytkownik java.nio.Filesodczytuje wszystkie wiersze pliku.

public String readFile() throws IOException {
        File fileToRead = new File("file path");
        List<String> fileLines = Files.readAllLines(fileToRead.toPath());
        return StringUtils.join(fileLines, StringUtils.EMPTY);
}

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

Myślę, że ma to niedogodność przy użyciu domyślnego kodowania platformy. W każdym razie +1 :)
OscarRyz

7
Wydaje mi się, że w końcu blok nie zna zmiennych zdefiniowanych w bloku try. javac 1.6.0_21 zgłasza błąd cannot find symbol.
ceving

Próbowałeś nawet własnego kodu? Zdefiniowałeś czytnik w bloku try / catch, więc nie będzie dostępny w bloku w końcu.
mauron85

2

Nie mogę jeszcze komentować innych wpisów, więc zostawię to tutaj.

Jedna z najlepszych odpowiedzi tutaj ( 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();
}
}

wciąż ma jedną wadę. Zawsze umieszcza znak nowej linii na końcu łańcucha, co może powodować pewne dziwne błędy. Moja sugestia to zmienić to na:

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

W pierwszym przypadku możesz dodać dodatkową linię na końcu. w drugim przypadku możesz pominąć jeden. Więc oba są w równym stopniu błędne. Zobacz ten artykuł
Patrick Parker

2

Po Ctrl + F'ing po skanerze, myślę, że rozwiązanie skanera również powinno zostać wymienione. W najłatwiejszy do odczytania sposób wygląda to tak:

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

Jeśli używasz Java 7 lub nowszej (i naprawdę powinieneś), rozważ użycie try-with-resources, aby ułatwić odczytanie kodu. Nigdy więcej zaśmiecania wszystkiego zaśmiecaniem. Ale to głównie wybór stylistyczny.

Zamieszczam to głównie dla uzupełnienia, ponieważ jeśli musisz to robić dużo, powinny być rzeczy Zamieszczam pliku java.nio.file.Files które powinny lepiej wykonywać tę pracę.

Moją sugestią byłoby użycie plików # readAllBytes (ścieżka), aby pobrać wszystkie bajty i podać je do nowego ciągu (zestaw znaków bajt []) zestaw aby uzyskać z niego ciąg, któremu można zaufać. Zestawy znaków będą dla ciebie wredne przez całe życie, więc uważaj teraz na te rzeczy.

Inni podali kod i inne rzeczy, a ja nie chcę kraść ich chwały. ;)


2

Korzystając z tej biblioteki , jest to jedna linia:

String data = IO.from(new File("data.txt")).toString();

1
jeśli wiersze w bibliotece nie są liczone.
Ari

2

Również jeśli plik znajduje się w słoiku, możesz również użyć tego:

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

Ścieżka powinna zaczynać się / na przykład od słoika

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

Następnie chcesz wywołać to w ten sposób:

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

2

W jednym wierszu (Java 8), zakładając, że masz czytnik:

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

2

Na podstawie odpowiedzi @ erickson możesz użyć:

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()]));
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.