Muszę czytać duży plik tekstowy o wielkości około 5-6 GB wiersz po wierszu, używając Java.
Jak mogę to zrobić szybko?
java
performance
file-io
io
garbage-collection
Manoj Singh
źródło
źródło
Odpowiedzi:
Często stosuje się wzorzec
Możesz odczytać dane szybciej, jeśli założysz, że nie ma kodowania znaków. np. ASCII-7, ale nie zrobi to dużej różnicy. Jest wysoce prawdopodobne, że to, co zrobisz z danymi, potrwa znacznie dłużej.
EDYCJA: Rzadziej stosowany wzór, który pozwala uniknąć
line
wycieku.AKTUALIZACJA: W Javie 8 możesz to zrobić
UWAGA: Musisz umieścić Strumień w bloku try-with-resource , aby upewnić się, że wywoływana jest metoda #close, w przeciwnym razie bazowy uchwyt pliku nigdy nie zostanie zamknięty, dopóki GC nie zrobi tego znacznie później.
źródło
for(String line = br.readLine(); line != null; line = br.readLine())
Przy okazji, w Javie 8 możesz zrobićtry( Stream<String> lines = Files.lines(...) ){ for( String line : (Iterable<String>) lines::iterator ) { ... } }
Co trudno nienawidzić.Spójrz na tego bloga:
źródło
DataInputStream
, a zły strumień zostaje zamknięty. Nie ma nic złego w samouczku Java i nie ma potrzeby cytowania takich śmieci internetowych.Po wydaniu Java 8 (marzec 2014 r.) Będziesz mógł korzystać ze strumieni:
Drukowanie wszystkich linii w pliku:
źródło
StandardCharsets.UTF_8
, używajStream<String>
dla zwięzłości i unikaj używania,forEach()
a zwłaszcza,forEachOrdered()
jeśli nie ma powodu.forEach(this::process)
, ale robi się brzydkie, jeśli piszesz bloki kodu jako lambdy wewnątrzforEach()
.forEachOrdered
, aby wykonać w kolejności. Pamiętaj, że w takim przypadku nie będziesz w stanie zrównoleglać strumienia, chociaż zauważyłem, że równoległość nie włącza się, chyba że plik ma tysiące linii.Oto przykład z pełną obsługą błędów i obsługą specyfikacji zestawów znaków dla wersji wcześniejszej niż Java 7. W Javie 7 można użyć składni try-with-resources, co czyni kod czystszym.
Jeśli chcesz tylko domyślnego zestawu znaków, możesz pominąć InputStream i użyć FileReadera.
Oto wersja Groovy z pełną obsługą błędów:
źródło
ByteArrayInputStream
karma literałem łańcuchowym ma wspólnego z czytaniem dużego pliku tekstowego?W Javie 8 możesz:
Kilka uwag: strumień zwrócony przez
Files.lines
(w przeciwieństwie do większości strumieni) musi zostać zamknięty. Z powodów wymienionych tutaj unikam używaniaforEach()
. Dziwny kod(Iterable<String>) lines::iterator
rzuca Strumień na Iterable.źródło
Iterable
tego kodu jest zdecydowanie brzydki, choć użyteczny. Potrzebuje obsady (tj.(Iterable<String>)
) Do pracy.for(String line : (Iterable<String>) lines.skip(1)::iterator)
Stream
funkcji, używanieFiles.newBufferedReader
zamiastFiles.lines
i wielokrotne wywoływaniereadLine()
ażnull
do użycia konstrukcji(Iterable<String>) lines::iterator
wydaje się znacznie prostsze…Co możesz zrobić, to zeskanować cały tekst za pomocą skanera i przejść przez tekst wiersz po wierszu. Oczywiście powinieneś zaimportować następujące elementy:
Skaner zasadniczo skanuje cały tekst. Pętla while służy do przechodzenia przez cały tekst.
Ta
.hasNextLine()
funkcja to wartość logiczna, która zwraca wartość true, jeśli w tekście jest jeszcze więcej wierszy. Ta.nextLine()
funkcja daje ci całą linię jako ciąg znaków, którego możesz użyć w dowolny sposób. SpróbujSystem.out.println(line)
wydrukować tekst.Uwaga dodatkowa: .txt to tekst typu pliku.
źródło
BufferedReader.readLine()
i poprosił o najlepszą metodę.FileReader nie pozwoli ci określić kodowania, użyj
InputStreamReader
zamiast tego, jeśli musisz to określić:Jeśli plik został zaimportowany z systemu Windows, może on mieć kodowanie ANSI (Cp1252), więc musisz określić kodowanie.
źródło
Udokumentowałem i przetestowałem 10 różnych sposobów odczytu pliku w Javie, a następnie uruchomiłem je ze sobą, odczytując je w plikach testowych od 1 KB do 1 GB. Oto najszybsze 3 metody odczytu plików do odczytu pliku testowego 1 GB.
Zauważ, że podczas testów wydajności nie wypisałem niczego na konsolę, ponieważ to naprawdę spowolniłoby test. Chciałem tylko przetestować prędkość odczytu.
1) java.nio.file.Files.readAllBytes ()
Testowane w Javie 7, 8, 9. To była ogólnie najszybsza metoda. Odczyt pliku 1 GB był konsekwentnie krótszy niż 1 sekunda.
2) java.nio.file.Files.lines ()
Zostało to pomyślnie przetestowane w Javie 8 i 9, ale nie będzie działać w Javie 7 z powodu braku obsługi wyrażeń lambda. Odczytanie pliku 1 GB zajęło około 3,5 sekundy, co plasuje go na drugim miejscu pod względem odczytu większych plików.
3) BufferedReader
Testowany do pracy w Javie 7, 8, 9. Odczytanie pliku testowego 1 GB zajęło około 4,5 sekundy.
Kompletne rankingi dla wszystkich 10 metod odczytu plików znajdują się tutaj .
źródło
System.out.print/println()
; zakładasz również, że plik zmieści się w pamięci w pierwszych dwóch przypadkach.W Javie 7:
źródło
StandardCharsets.UTF_8
aby uniknąć zaznaczonego wyjątku wCharset.forName("UTF-8")
W Javie 8 istnieje również alternatywa do używania
Files.lines()
. Jeśli źródłem wejściowym nie jest plik, ale coś bardziej abstrakcyjnego, takiego jak aReader
lub anInputStream
, możesz przesyłać strumieniowo linie za pomocą metodyBufferedReader
slines()
.Na przykład:
wywoła
processLine()
każdą linię wejściową odczytaną przezBufferedReader
.źródło
Do odczytu pliku w Javie 8
źródło
Możesz użyć klasy skanera
źródło
Scanner
jest w porządku, ale ta odpowiedź nie zawiera pełnego kodu do prawidłowego użycia.BufferedReader.readLine()
pewnością jest to kilka razy szybsze. Jeśli uważasz inaczej, podaj uzasadnienie.Musisz użyć tej
readLine()
metody wclass BufferedReader
. Utwórz nowy obiekt z tej klasy i uruchom na nim tę metodę i zapisz go w ciągu.BufferReader Javadoc
źródło
Jasny sposób na osiągnięcie tego,
Na przykład:
Jeśli masz
dataFile.txt
w bieżącym kataloguDane wyjściowe jak poniżej
źródło
Java 9:
źródło
System.getProperty("os.name").equals("Linux")
==
!Mi to pasuje. Mam nadzieję, że ci to pomoże.
źródło
Możesz użyć strumieni, aby zrobić to dokładniej:
źródło
Zwykle wykonuję procedurę czytania od razu:
źródło
Możesz użyć tego kodu:
źródło
Korzystając z pakietu org.apache.commons.io , zwiększył on wydajność, szczególnie w starszym kodzie, który używa Java 6 i niższych wersji.
Java 7 ma lepszy interfejs API z mniejszą obsługą wyjątków i bardziej przydatnymi metodami:
Maven
źródło
Możesz także użyć Apache Commons IO :
źródło
FileUtils.readLines(file)
jest przestarzałą metodą. Dodatkowo metoda wywołujeIOUtils.readLines
, która korzysta z BufferedReader i ArrayList. To nie jest metoda linia po linii, a na pewno nie taka, która byłaby praktyczna przy odczytywaniu kilku GB.