Failist lugemine
Sissejuhatus
Javas on võimalik mitmel erineval viisil failist sisu lugeda. Õige valik sõltub faili suurusest ja sellest, mida sisuga soovitakse teha.
Väikeste failide puhul, kus terve sisu mahub mugavalt mällu, pakub Files abiklass mugavaid ühe-rea meetodeid.
Suurte failide puhul, kus kogu sisu korraga lugemine ei ole otstarbekas, võimaldab BufferedReader töödelda sisu rida-rea haaval ilma kogu faili mällu laadimata.
Kõik lugemistoimingud võivad visata IOException-i.
Antud peatüki näidetes käsitletakse faili sulgemist lihtsa try-catch ploki sees selguse huvides.
Praktikas sedasi ei tohiks seda teha, eelistama peaks Try-with-resources lähenemist.
Terve faili korraga lugemine
Files.readString
Files.readString loeb terve faili sisu korraga läbi ning salvestab tulemused sõnesse:
Path path = Path.of("catalogue.txt");
try {
String content = Files.readString(path);
System.out.println(content);
} catch (IOException e) {
System.err.println("Could not read file: " + e.getMessage());
}
Files.readAllLines
Sarnaselt eelnevale, loeb Files.readAllLines terve faili sisu korraga läbi.
Erinevus seisneb selles, et sõne asemel tagastatakse järjend sõnedest, kus iga element tähistab üht rida failis.
Path path = Path.of("books.txt");
try {
List<String> lines = Files.readAllLines(path);
for (String line : lines) {
System.out.println(line);
}
} catch (IOException e) {
System.err.println("Could not read file: " + e.getMessage());
}
Millal neid meetodeid kasutada
Nii Files.readString kui ka Files.readAllLines loevad kogu faili sisu enne tagastamist mällu.
See sobib hästi failide puhul, mis on mõne megabaidi suurused - konfiguratsioonifailid, väikesed andmefailid, logiväljavõtted.
Failide puhul, mis võivad olla väga suured, võib kogu sisu korraga laadimine viia mälupuuduseni.
Sellistel juhtudel on parem valik BufferedReader.
Rida-rea haaval lugemine BufferedReader-iga
BufferedReader mähkib teise lugeja endasse ja lisab puhverdamise, mis tähendab, et see loeb kettalt korraga suurema andmehulga, mitte ühe märgi kaupa. See teeb selle tõhusaks sõltumata faili suurusest.
Path path = Path.of("books.txt");
try {
BufferedReader reader = Files.newBufferedReader(path);
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
reader.close();
} catch (IOException e) {
System.err.println("Could not read file: " + e.getMessage());
}
readLine() tagastab järgmise rea ilma reavahetusmärgita või null, kui on jõutud faili lõppu.
Sedasi reader.close() kutsumine try-catch plokis ei ole turvaline.
Kui enne close()-ni jõudmist visatakse erind, jääb lugeja sulgemata ja tekib resurssileke.
Siin on seda kasutatud lihtsuse ja selguse mõttes, praktikas peaksite kasutama try-with-resources võtet, mis sulgeb lugeja automaatselt ka erindi korral.
BufferedReader koos Stream API-ga
BufferedReader pakub lines() meetodit, mis tagastab faili sisu Stream<String> kujul.
See integreerub otse Stream API-ga, võimaldades filtreerimist, teisendamist ja kogumist ühesainsas voos:
Path path = Path.of("books.txt");
try (BufferedReader reader = Files.newBufferedReader(path)) {
List<String> filtered = reader.lines()
.filter(line -> !line.isBlank())
.toList();
} catch (IOException e) {
System.err.println("Could not read file: " + e.getMessage());
}
Samuti on võimalik sama saavutada ka lühema käsuga, mis avab lugeja ja tagastab voo otse:
try (Stream<String> lines = Files.lines(Path.of("books.txt"))) {
List<String> filtered = lines
.filter(line -> !line.isBlank())
.toList();
} catch (IOException e) {
System.err.println("Could not read file: " + e.getMessage());
}
Vooga failist lugemise eelised
Files.readAllLines loeb kõik read enne töötlemise algust mällu.
Files.lines poolt tagastatud voog on laisk ehk see loeb kettalt andmeid ainult siis, kui neid tegelikult vaja läheb.
Miljonite ridadega faili puhul on mälukasutuse erinevus märkimisväärne.
Ka väiksemate failide puhul annab voopõhine lähenemine sageli puhtama koodi, kui eesmärk on ridade filtreerimine ja teisendamine.