Fájlkezelés Javában

Kategória: Java standard könyvtárak.

Beolvasás fájlból kezdetben

A fájlkezelés a Java-ban hosszú utat járt be, és ez egy jó példa arra, hogy hogyan lehet valamit nagyon elrontani. Kezdetben létrehoztak egy rendkívül elbonyolított InputStream és OutputStream hierarchiát, és erre építették rá a fájlműveleteket is. Csak érdekességképpen említem meg, hogy kezdetben hogyan lehetett ezt megoldani:

import java.io.*;
import java.nio.charset.Charset;
 
class FileInputExample {
    public static void main(String[] args) {
        BufferedReader dis = null;
        String line = null;
        try {
            File file = new File("mydata.txt");
            FileInputStream fis = new FileInputStream(file);
            BufferedInputStream bis = new BufferedInputStream(fis);
            InputStreamReader isr = new InputStreamReader(bis, Charset.defaultCharset());
            dis = new BufferedReader(isr);
            while ((line = dis.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (dis != null) {
                try {
                    dis.close();
                } catch (IOException ioe) {
                    ioe.printStackTrace();
                }
            }
        }
    }
}

Tehát szükség volt a következőkre: File, FileInputStream, BufferedInputStream, InputStreamReader, kétszintű hibakezelés, hogy csak az alap példa problémáit említsem. Ezzel valójában két probléma volt:

  • Lehetetlen volt megjegyezni, minden egyes alkalommal rá kellett keresni.
  • Szinte sikított a változásért, ami be is következett, viszont ezzel még komplikáltabb lett a rendszer. A felülről kompatibilitás kényszere miatt ugyanis a régi rendszer megmaradt (ráadásul részben depricated lett, részben nem; tehát az eredeti példa ráadásul nem is pont így nézett ki mint a fent megadott), és megjelent az új, egyszerűsített változat is.
  • Ugyanakkor idővel további javításokat tettek bele, ami egyrészt jó, másrészt tovább fokozta a kuszaságot.

Ha valaki el szeretne mélyedni az input és output stream-ek világában, annak ajánlom elrettentésül az ezen az oldalon található táblázatot: http://tutorials.jenkov.com/java-io/overview.html, a kapcsolódó oldalakat, valamint a https://www.javatpoint.com/java-io oldalt.

Kiírás fájlba kezdetben

A kezdeti fájlba írás is eléggé komplikált (ráadásul hibásan működik, legalábbis nálam):

import java.io.*;
 
class FileInputExample {
    public static void main(String[] args) throws IOException {
        FileOutputStream fos = new FileOutputStream("test.txt");
        BufferedOutputStream bos = new BufferedOutputStream(fos);
        DataOutputStream outStream = new DataOutputStream(bos);
        outStream.writeUTF("Hello");
        outStream.writeUTF(" world");
        outStream.close();
    }
}

Fájlból olvasás a Scanner osztály segítségével

A Scanner osztály a Java 1.5-ben jelent meg, és jelentős mértékben leegyszerűsítette a beolvasást:

import java.io.*;
import java.util.Scanner;
 
class FileInputExample {
    public static void main(String[] args) throws IOException {
        File file = new File("mydata.txt");
        Scanner sc = new Scanner(file);
        while (sc.hasNextLine()) {
            System.out.println(sc.nextLine());
        }
        sc.close();
    }

Sőt, ha beállítjuk, hogy az elválasztó karakter az alapértelmezett új sor helyett a fájl vége legyen, akkor egyből be tudjuk olvasni, ciklus nélkül:

import java.io.*;
import java.util.Scanner;
 
class FileInputExample {
    public static void main(String[] args) throws IOException {
        File file = new File("mydata.txt");
        Scanner sc = new Scanner(file);
        sc.useDelimiter("\\Z");
        System.out.println(sc.next());
        sc.close();
    }
}

A try-with-resources módszerrel a fenti lehetőség így néz ki:

import java.io.*;
import java.io.FileNotFoundException;
import java.util.Scanner;
 
class FileInputExample {
    public static void main(String[] args) {
        try (Scanner sc = new Scanner(new File("mydata.txt"))) {
            while (sc.hasNextLine()) {
                System.out.println(sc.nextLine());
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }
}

Kiírás a PrintWriter osztály segítségével

A FileWriter és a PrintWriter osztályok használata valamelyest egyszerűsíti a kiírást, ráadásul a lehetőségeket is javítja:

import java.io.*;
 
class FileInputExample {
    public static void main(String[] args) throws IOException {
        FileWriter fileWriter = new FileWriter("test.txt");
        PrintWriter printWriter = new PrintWriter(fileWriter);
        printWriter.println("Hello world");
        printWriter.printf("This is a text: %s, and this is an integer: %d.", "apple", 5);
        printWriter.close();
    }
}

A Files osztály használata fájlműveletekre

import java.io.*;
import java.nio.file.*;
 
class FileInputExample {
    public static void main(String[] args) throws IOException {
        Path path = Paths.get("test.txt");
        Files.write(path, "Hello".getBytes());
        System.out.println(Files.readAllLines(path));
    }
}

Ennek is vannak nehézségei (a Path szükségessége ahelyett, hogy elég lenne megadni a fájlnevet; Stringet nem tudunk kiírni, csak bájtokat), viszont az eredmény kompakt, ugyanaz az osztály használható kiírásra és beolvasásra is, ráadásul a Files számos egyéb fájlműveletet definiál: fájl és könyvtár létrehozása, létezésnek ellenőrzése, másolás, törlés stb.

Olvasás a resources könyvtárból

Az src/java/resources/ könyvtár belekerül a végeredmény jar fájlba. Onnan az alábbi módon tudunk olvasni:

import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.List;
 
public class MyResourceReader {
 
    public static void main(String[] args) {
        MyResourceReader myResourceReader = new MyResourceReader();
        myResourceReader.readFileFormResourcees();
    }
 
    public void readFileFormResourcees() {
        try {
            ClassLoader classLoader = getClass().getClassLoader();
            URL resource = classLoader.getResource("mydata.txt");
            File file = new File(resource.toURI());
            List<String> lines = Files.readAllLines(file.toPath(), StandardCharsets.UTF_8);
            lines.forEach(System.out::println);
        } catch (URISyntaxException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Egyéb fájlműveletek

Ennyi bántást követően lássunk egy olyan megoldást, ami mintaként kellene hogy funkcionáljon minden programozó számára! A Files mellett az eredeti File osztály is definiál alap fájl műveleteket, ráadásul - véleményem szerint - jóval egyszerűbben és természetesebben. Lássunk erre is egy példát!

import java.io.*;
 
class FileInputExample {
    public static void main(String[] args) {
        File file = new File("mydata.txt");
        System.out.println(file.exists());
        file.delete();
        System.out.println(file.exists());
    }
}

Kell magyarázni, hogy mit hajt végre ez a kód? Szerintem nem! Néhány példa a File által nyújtott műveletekre, magyarázat nélkül, ugyanis mindegyik pont azt hajtja végre, amire a neve alapján számítunk: canRead(), canWrite(), createNewFile(), delete(), exists(), getName(), getAbsolutePath, length(), list() (ez a könyvtárat listázza ki, eredménye String[]), mkdir().

A Java fájlkezelési lehetőségeknek csak egy részét érintettük, de ez elég ahhoz, hogy a legfontosabb feladatokat végre tudjuk hajtani.

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License