Update: Dankzij Thom, een oud-medestudent en vriend van mij, is deze tutorial nu verbeterd. Thom, je bent een held!
Gegroet Dataridders,
Welkom bij de tweede Java data tutorial. Dit keer zal ik jullie leren hoe je data die je hebt opgeslagen in een bestand als tekst kunt laden. Opnieuw geldt hierbij: Dit is hoe ik het altijd gedaan heb en niet per se de enige noch de beste methode. Voor deze tutorial gelden dezelfde benodigdheden als bij de vorige:
- Java SDK geïnstalleerd op je systeem
- Een Java IDE, bijvoorbeeld Netbeans of Eclipse
- Basiskennis van programmeren met Java
Om te beginnen zullen we weer eerst onze methode definiëren. We willen een functie die als input een bestandslocatie filePath voor een tekstbestand neemt en dan als output ons de tekst in dat bestand geeft in een ArrayList van String gegevens. En net zoals de vorige keer mag de functie weer static zijn, aangezien er geen object nodig is om hem te gebruiken. Dit ziet er dan als volgt uit:
public static ArrayList<String> loadData(String filePath)
{
ArrayList<String> loadedData = new ArrayList();
return loadedData;
}
Op dit moment wordt er nog niets geladen en is de ArrayList die wordt gereturned door onze methode nog leeg. Laten we daar verandering in brengen. Om onze data te laden zullen we gebruik maken van een FileReader. Moet ik echt uitleggen wat deze class doet?
Hij leest bestanden.
Maar een FileReader is niet erg efficiënt voor onze toepassing. Daarom hebben we nog een class nodig: BufferedReader. Een BufferedReader neemt als input een ander Reader object, en het resultaat is een Reader met byte-buffering, welke efficiënter is.
Echter, voor onze toepassing maakt die extra efficiëntie niet veel uit. We zijn meer geïnteresseerd in de readLine() methode van de BufferedReader, maar daarover later meer. Dat ziet er dan als volgt uit:
FileReader fileReader = new FileReader(filePath);
BufferedReader bufferedReader = new BufferedReader(fileReader);
Wat in Google’s naam is byte-buffering?! Eerlijk gezegd heb ik geen idee. Sterker nog, voordat ik deze tutorial maakte wist ik niet eens wat een BufferedReader was, waarom ik het moest gebruiken en waarom het een FileReader als input nam. Maar ik wist wat Google was en ik wist wat copy-paste was en dus ik wist hoe ik data moest laden.
Ik wilde voor deze tutorial echter toch wel iets meer kunnen uit leggen over het onderliggende proces dan dat, en dus heb ik maar wat onderzoek gedaan. Wil je meer weten over byte-buffering dan heb je twee opties: Wacht totdat ik besluit een tutorial of blog post te doen over byte-buffering, of doe wat ik doe als ik iets niet weet: Google het!
Genoeg intermezzo, verder met programmeren!
Net zoals bij de constructor van de PrintWriter moeten er weer wat Exceptions opgevangen worden bij de initialisatie van de FileReader. We zullen de code dus weer met try en catch omringen.
try
{
fileReader = new FileReader(filePath);
BufferedReader bufferedReader = new BufferedReader(fileReader);
}
catch (FileNotFoundException ex)
{
Logger.getLogger(DataLoadingTutorial.class.getName()).log(Level.SEVERE, null, ex);
}
Nu kunnen we de BufferedReader gebruiken om ons bestand te lezen. We doen dat met de eerder genoemde readLine() functie van BufferedReader en een while loop. De readline() functie stelt ons in staat hele regels tegelijk te lezen in ons programma, iets wat met FileReader niet kan. Zolang ons bestand nieuwe regels met tekst bevat zal readLine() ons deze geven. We willen voor onze while loop dan de output van readLine() toe blijven voegen aan onze ArrayList totdat readLine() ons een lege regel geeft. Dit ziet er dan als volgt uit:
String line;
while ((line = bufferedReader.readLine()) != null)
{
loadedData.add(line);
}
Ook readLine genereert echter weer wat vervelende Exceptions die moeten afgehandeld worden, IOExceptions dit keer. We zullen dus een catch-clausule moeten toevoegen:
catch (FileNotFoundException ex)
{
Logger.getLogger(DataLoadingTutorial.class.getName()).log(Level.SEVERE, null, ex);
}
catch (IOException ex)
{
Logger.getLogger(DataLoadingTutorial.class.getName()).log(Level.SEVERE, null, ex);
}
De Logger code is overigens weer automatisch gegenereerd en wordt gebruikt om de foutmeldingen in het log scherm te weergeven maar is niet essentieel, en kan weggelaten of bijvoorbeeld vervangen worden door een “System.out.println()” met tekst naar keuze.
In principe werkt deze code zo dan ook, maar dankzij Thom weten we nu hoe het beter kan. Ten eerste was ik vergeten om de reader te sluiten nadat de File gelezen was. De meest elegante methode om dit te doen met Java is door de constructor te verplaatsen naar de try opening zoals hieronder:
try (BufferedReader bufferedReader = new BufferedReader(new FileReader(filePath)))
{
String line;
while ((line = bufferedReader.readLine()) != null)
{
loadedData.add(line);
}
}
Dit zorgt ervoor dat de reader gesloten wordt, zelfs als er een Exception plaatsvindt. Hetzelfde principe geldt overigens voor de PrintWriter van de savefunctie.
try (PrintWriter writer = new PrintWriter(filePath, StandardCharsets.UTF_8.name()))
Daarnaast is een FileNotFoundException een subclass van een IOException en kunnen we de catch voor de FileNotFoundException dus weglaten, en alles met de IOException opvangen.
catch (IOException ex)
{
Logger.getLogger(DataLoadingTutorial.class.getName()).log(Level.SEVERE, null, ex);
}
Tot slot is er blijkbaar nog een alternatieve, makkelijkere manier om gegevens te laden met Java 8, door gebruik te maken van Streams met de Files class.
// Alternative: Java 8 way with Streams
try
{
System.out.println(“”);
System.out.println(“Inhoud:”);
Files.lines(Paths.get(PATH + “test.txt”)).forEach(System.out::println);
}
catch (IOException ex)
{
Logger.getLogger(DataLoadingTutorial.class.getName()).log(Level.SEVERE, null, ex);
}
Ja, ik weet het, waarom niet gewoon deze tutorial daarmee beginnen?! Maar ik zei al, ik ging jullie laten zien hoe IK het deed, en ik kende deze manier nog niet. Bovendien kan ik jullie er op het moment ook weinig over deze alternatieve methode uitleggen, en denk ik dat het proces dat ik heb uitgestippeld in mijn methode nog steeds heel leerzaam is. In de toekomst zal ik deze manier echter misschien ook gaan gebruiken.
En klaar is kees!
Zolang je nu maar de juiste bestandslocatie opgeeft zal deze code alle regels uit je tekstbestand in een ArrayList zetten. De volledige code, inclusief extra code voor het testen is te vinden op https://github.com/SamsonCodes/DataLoadingTutorial. Trouwens, mocht je het nog niet weten, de Java files binnen een Netbeans project zijn te vinden in de src folder. In dit geval is er slechts een Java file, en die is te vinden in de dataloadingtutorial folder (in Netbeans: package) binnen deze src folder.
Dat is alles voor nu,
Tot de volgende keer!