Čtení a zápis

Proudy

Pro čtení a zápis do souborů slouží proudy (Streams). Proudy umožňují pouze sekvenční zpracování, tj. data přicházejí v pořadí v jakém byla zapsána.

Práce s proudem

Standardní práce s proudem vypadá takto:

  1. otevření streamu,
  2. čtení/zápis dat z/do streamu,
  3. zavření streamu

Java rozlišuje proudy znaků (16 bitové) a proudy bytů (8 bitové). Proudy bytů obsahují ve svém názvu Stream a směr toku dat Input/Output vzhledem k programu. Znakové proudy jsou označeny Reader (vstupní) a Writer (výstupní). Proudy mohou pracovat s různými zdroji, pro jejich rozlišení je uvedeno v názvu třídy. Pro operace se soubory je to klíčové slovo File. Znakový proud pro čtení ze souboru je FileReader, bytový proud pro zápis do souboru je FileOutputStream.

Čtení z bytového proudu:

    FileInputStream fis = new FileInputStream("vstup.bin");
    int i;
    while ((i = fis.read()) != -1)   // hodnota -1 znamena konec proudu
       System.out.println(i);
    fis.close();
Zápis do bytového proudu:
    FileOutputStream fos = new FileOutputStream("vystup.bin");
    for (int i=0; i < 10; i++)
       fos.write(i);
    fos.close();
Čtení po znacích ze znakového proudu:
    FileReader fr = new FileReader("vstup.txt");
    int c;
    while ((c = fr.read()) != -1)   // hodnota -1 znamena konec proudu
       System.out.print((char) c);
    fr.close();
Zápis řetězce do znakového proudu:
    FileWriter fw = new FileWriter("vystup.txt");
    fw.write(args[0]);
    fw.close();

Spojování proudů

Proudy lze vzájemně spojovat, tj. výstup jednoho proudu lze napojit na vstup druhého proudu. Proudy lze rozdělit na 2 skupiny: proudy pro přímé napojení na zdroj dat (soubor) a proudy pro zpracování výstupu z jiného proudu. Potřebujeme-li propojit dva proudy, předáme první proud jako parametr do konstruktoru druhého proudu. Např. DataInputStream a DataOutputStream slouží pro čtení a zápis primitivních datových typů:

    double price = ...;
    int unit = ...;
 
    DataOutputStream dos = new DataOutputStream(new FileOutputStream("invoice.txt"));
    dos.writeDouble(price);
    dos.writeChar('\t');
    dos.writeInt(units);
    dos.close();
    DataInputStream dis = new DataInputStream(new FileInputStream("invoice.txt"));
    price = dis.readDouble();
    dis.readChar();
    unit = dis.readInt();
    dis.close();

Třídy InputStreamReader a OutputStreamWriter

Proudy InputStreamReader a OutputStreamWriter mají zvláštní postavení. Slouží ke konverzi bytů na znaky a opačně. InputStreamReader čte byty z bytového streamu, konvertuje je na znaky a ty předává na výstup. Do proudu typu OutputStreamWriter zapisujeme znaky, proud je konvertuje na byty a ty předává na výstup.

Bufferované proudy

Další skupinou proudů jsou bufferované proudy, které umožňují číst několik bytů či znaků najednou. Např. ke čtení textu po řádcích slouží proud BufferedReader:

    String s;
    InputStreamReader isr = new InputStreamReader(System.in);
    BufferedReader br = new BufferedReader(isr);
    while ((s = br.readLine()) != null) {
        System.out.println(s);
    }
    br.close();

Třída RandomAccessFile

Třída RandomAccessFile umožňuje přistupovat k datům v souboru v libovolném pořadí(random, zde neznamená náhodné, protože nepotřebujeme náhodně vybraná data, ale jedna konkrétní z námi určené pozice).

    byte[] b = "approved".getBytes();
    RandomAccessFile raf = new RandomAccessFile("raf.txt","rw");
    raf.seek(24);            // nastav pozici
    raf.write(b);            // zapis na pozici
    raf.close();

Serializace

Serializace slouží k čtení/zápisu objektů, které jsou již složitější než základní datové typy. Proces serializace je zajišťován třídami ObjectInputStream a ObjectOutputStream, které obsahují metody readObject() a writeObject(). Pomocí nich lze zapsat nebo přečíst všechna data, která daný objekt v Javě potřebuje. Tatko lze zapisovat objekty tříd, které implementují rozhraní java.io.Serializable. Toto je však splněno pro většinu tříd z Java Core API. Pokud potřebujeme takto pracovat s objekty vlastní třídy, je nutné tuto implementaci zajistit, což není obtížné, neboť rozhraní nemá žádné metody. Vlastní třída však musí mít datové prvky ze základních datových typů nebo tzpů, které již implementovaly rozhraní Serializable.