Demo: obsługa plików różnych typów

Przykład ilustruje wykorzystanie: tablicy obiektów, plików tekstowych (BufferedReader, BufferedWriter), plików binarnych (RandomAccessFile), plików obiektów (ObjectInputStrea, ObjectOutputStrea), klas StringTokenizer, StringBuilder, funkcji String.split. Można pobrać projekt NetBeans w formacie zip. Uwaga, program nie waliduje zawartości plików i jest podatny na ich niepoprawną zawartość.

// Program prezentuje metody wykonywania operacji na plikach tekstowych,
// obiektów i binarnych. Operacje dotyczą informacji o samochodach, odczytywane
// dane są zapisywane w tablicy obiektów, operacje zapisu danych pobierają dane
// a tej samej tablicy.
// UWAGA: nie jest realizowana kontrola poprawności odczytywanych danych, błędy
// w pliku będą skutkować błędami w działaniu programu.

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.RandomAccessFile;
import java.io.Serializable;
import java.util.StringTokenizer;

// Obiekty tej klasy będą zapamiętywane w tablicy, będą zapisywane i odczytywane
// do/z plkików różnych typów. Implementacja interfejsu Serializable jest
// konieczna dla obsługi plików obiektów, nie trzeba jej używać dla plików
// tekstowych i binarnych.
class Auto implements Serializable {
    String marka;
    String model;
    double pojemnosc;

    void pokaz() {
        System.out.println("    Marka: " + marka);
        System.out.println("    Model: " + model);
        System.out.println("Pojemność: " + pojemnosc);
    }
}

// Klasa prezentująca różne rodzaje plików i spososby realizacji operacji
// zapisu i odczytu
class EwidencjaAut {
    // Do tej tablicy będą odczytywane dane, z tej tablicy będą pobierane dane
    // zapisywane do plików
    private Auto[] auta = null;

    void pokaz() {
        if (auta == null) return;
        for (int nrAuta = 0; nrAuta < auta.length; ++nrAuta) {
            System.out.println("Auto nr: " + (nrAuta + 1));
            auta[nrAuta].pokaz();
        }
    }
    // Pliki tekstowe

    // Odczyt danych do tablicy z pliku tekstowego
    // Założenie pierwsza linia zawiera liczbę linii zawierających opisy aut
    // W kolejnych liniach:
    // marka model pojemność_silnika
    boolean oczytZPlikuTekstowego(String nazwaPliku) {
        try (BufferedReader plik = new BufferedReader(new FileReader(nazwaPliku))) {
            // Odczytanie liczby aut
            String linia = plik.readLine();
            auta = new Auto[Integer.parseInt(linia)];

            // Odczyt kolejnych linii opisu auta
            for (int i = 0; i < auta.length; ++i) {
                auta[i] = new Auto();  // Utwórz obiekt
                linia = plik.readLine(); // Czytaj linię

                // Prezentacja klasy StringTokenizer
                // Utworzenie tokenizera dla linii, separator to spacja
                StringTokenizer tok = new StringTokenizer(linia, " ");
                // Wyodrębnienie trzech elementów z linii
                auta[i].marka = tok.nextToken();
                auta[i].model = tok.nextToken();
                auta[i].pojemnosc = Double.parseDouble(tok.nextToken());
            }
        } catch (IOException e) {
            return false;
        }
        return true;
    }

    // Zapis danych z tablicy aut do pliku tekstowego
    // Założenie pierwsza linia zawiera liczbę linii zawierających opisy aut
    // W kolejnych liniach:
    // marka model pojemność_silnika
    boolean zapisDoPlikuTekstowego(String nazwaPliku) {
        try (BufferedWriter plik = new BufferedWriter(new FileWriter(nazwaPliku))) {
            // Zapis liczby aut do pliku
            plik.write(String.valueOf(auta.length));
            plik.newLine();

            // Zapis kolejnych aut
            for (int i = 0; i < auta.length; ++i) {
                // Prezentacja klasy StringBuilder
                StringBuilder linia = new StringBuilder();
                linia.append(auta[i].marka);
                linia.append(" ");
                linia.append(auta[i].model);
                linia.append(" ");
                linia.append(auta[i].pojemnosc);
                // Zapis linii "sklejonej" przez obiekt klasy StringBuilder
                plik.write(linia.toString());
                plik.newLine();
            }
        } catch (IOException e) {
            return false;
        }
        return true;
    }

    // Pliki obiektów

    // Zapis danych z tablicy aut do pliku obiektów
    // Założenie: plik rozpoczyna dana typu int, określająca liczbę aut
    boolean zapisDoPlikuObiektow(String nazwaPliku) {
        try (ObjectOutputStream plik = new ObjectOutputStream(new FileOutputStream(nazwaPliku))) {
            // Zapis liczby aut
            plik.writeInt(auta.length);
            // Zapis kolejnych elementów tablicy
            for (Auto auto : auta)
                plik.writeObject(auto);
        } catch (IOException e) {
            return false;
        }

        return true;
    }

    // Odczyt danych z pliku obiektów, utworzenie tablicy
    // Założenie: plik rozpoczyna dana typu int, określająca liczbę aut
    boolean odczytZPlikuObiektow(String nazwaPliku) {
        try (ObjectInputStream plik = new ObjectInputStream(new FileInputStream(nazwaPliku))) {
            // Odczyt liczby aut i utworzenie tablicy
            auta = new Auto[plik.readInt()];
            // Odczyt kolejnych aut, ich liczba jest znana
            for (int i = 0; i < auta.length; ++i)
                auta[i] = (Auto) plik.readObject();
        } catch (IOException e) {
            return false;
        } catch (ClassNotFoundException e) {
            return false;
        }
        return true;
    }

    // Pliki binarne (pliki danych)

    // Zapis danych z tablicy aut do pliku binarnego
    // Założenie: plik rozpoczyna dana typu int, określająca liczbę aut
    public boolean zapisDoPlikuBinarnego(String nazwaPliku) {
        try (RandomAccessFile plik = new RandomAccessFile(nazwaPliku, "rw")) {
            // Zapis liczby aut do pliku
            plik.writeInt(auta.length);

            // zapis kolejnych aut
            for (Auto auto : auta) {
                plik.writeUTF(auto.marka);
                plik.writeUTF(auto.model);
                plik.writeDouble(auto.pojemnosc);
            }
        } catch (IOException e) {
            return false;
        }
        return true;
    }
    // Pliki binarne (pliki danych)

    // Odczyt danych z pliku binarnego, utworzenie tablicy
    // Założenie: plik rozpoczyna dana typu int, określająca liczbę aut
    boolean odczytZPlikuBinarnego(String nazwaPliku) {
        try (RandomAccessFile plik = new RandomAccessFile(nazwaPliku, "rw")) {
            // Odczyt liczby aut i utworzenie tablicy
            auta = new Auto[plik.readInt()];
            // Odczyt kolejnych aut, ich liczba jest znana
            for (int i = 0; i < auta.length; ++i) {
                auta[i] = new Auto();  // Utwórz obiekt
                auta[i].marka = plik.readUTF();
                auta[i].model = plik.readUTF();
                auta[i].pojemnosc = plik.readDouble();
            }
        } catch (IOException e) {
            return false;
        }
        return true;
    }
}

public class AutaNaPlikiRoznychTypow {
    public static void main(String[] args) {
        EwidencjaAut ew = new EwidencjaAut();

        System.out.print("Odczyt z pliku tekstowego: ");
        if (ew.oczytZPlikuTekstowego("auta.txt")) {
            System.out.println("OK");
            ew.pokaz();
        } else
            System.out.println("Błąd");

        System.out.print("Zapis do pliku tekstowego: ");
        if (ew.zapisDoPlikuTekstowego("auta.bak"))
            System.out.println("OK");
        else
            System.out.println("Błąd");

        System.out.print("Zapis do pliku obiektów: ");
        if (ew.zapisDoPlikuObiektow("auta.obj"))
            System.out.println("OK");
        else
            System.out.println("Błąd");

        System.out.print("Odczyt z pliku obiektów: ");
        if (ew.odczytZPlikuObiektow("auta.obj")) {
            System.out.println("OK");
            ew.pokaz();
        } else
            System.out.println("Błąd");

        System.out.print("Zapis do pliku binarnego: ");
        if (ew.zapisDoPlikuBinarnego("auta.bin"))
            System.out.println("OK");
        else
            System.out.println("Błąd");

        System.out.print("Odczyt z pliku binarnego: ");
        if (ew.odczytZPlikuBinarnego("auta.bin")) {
            System.out.println("OK");
            ew.pokaz();
        } else
            System.out.println("Odczyt z pliku obiektów: Błąd");

        wyeksportujZAkcyza("akcyza.txt", "auta.bin");
        ocenOplacalnosc("akcyza");
    }

    // Dodatkowe przykłady. W tych funkcjach nie korzystamy z tablicy aut ani
    // obiektów klasy Auto, nie korzystamy z obiektu klasy StringBuilder.

    // Funkcja odczytuje dane pojazdu z pliku binarnego o nazwie nazwaBinarnego
    // (format jak w ewidencji aut) i zapisuje do pliku (tekstowego o nazwie
    // nazwaTekstowego) odczytane dane uzupełnione informacją o akcyzie.
    // Auta o pojemności poniżej 2000 mają akcyzę 3.1% a auta o pojemności
    // większej niż 2000 18.65. Procent akcyzy należy dopisać  na końcu każdej
    // linii zapisywanej do pliku tekstowego:
    // Mercedes SL 3500 18.6
    // Fiat 126p 650 3.1
    // Liczba aut nie ma być zapisywana do pliku tekstowego.
    static boolean wyeksportujZAkcyza(String nazwaTekstowego, String nazwaBinarnego) {
        try (
                BufferedWriter plikTxt = new BufferedWriter(new FileWriter(nazwaTekstowego));
                RandomAccessFile plikBin = new RandomAccessFile(nazwaBinarnego, "rw")
        ) {
            String marka, model, akcyza;
            double pojemnosc;
            // Odczyt liczby aut
            int liczbaAut = plikBin.readInt();
            // Odczyt kolejnych aut
            for (int i = 0; i < liczbaAut; ++i) {
                marka = plikBin.readUTF();
                model = plikBin.readUTF();
                pojemnosc = plikBin.readDouble();
                akcyza = (pojemnosc < 2000) ? "3.1" : "18.6";
                plikTxt.write(marka + " " + model + " " + pojemnosc + " " + akcyza);
                plikTxt.newLine();
            }
        } catch (IOException e) {
            return false;
        }
        return true;
    }

    // Funkcja otrzymuje jako parametr nawę pliku BEZ rozszerzenia (np. "plik").
    // Po dopisaniu do tej nazwy rozszerzenia ".txt" (powstaje "plik.txt") funkcja
    // otwiera do odczytu plik o tej nazwie, każda linia
    // z pliku ma następujący układ:
    // <marka> <model> <pojemnosc> <procent akcyzy>
    // np.:
    // Mercedes SL 3500 18.6
    // Fiat 126p 650 3.1
    // Zadaniem funkcji jest zapisanie do drugiego pliku tekstowego raportu
    // opłacalności, dla przykładu powyżej będzie to:
    // Mercedes SL drogo
    // Fiat 126p opłacalnie
    // Za auto opłacalne uważa się takie, którego pojemność jest mniejsza od 2000.
    // Nazwa pliku raportu powstaje poprzez doklejenie do nazwy przekazanej
    // parametrem nazwa rozszerzenia "rpt". Dodatkowo funkcja zapisuje do pliku
    // binarnego pary pojemnosci akcyza dla każdego auta. Nazwa pliku binarnego
    // powstaje poprzez doklejenie do nazwy przekazanej parametrem nazwa
    // rozszerzenia "bin". Rezulataem funkcji ma być liczba aut zklasyfikowanych
    // jako opłacalne, lub -1 gdzy wystąpoł błąd wejści-wyjścia.
    // Program używa funkcji split zamiast klasy StringTokenizer.
    static int ocenOplacalnosc(String nazwa) {
        String nazwaTekstowego = nazwa + ".txt";
        String nazwaBinarnego = nazwa + ".bin";
        String nazwaRaportu = nazwa + ".rpt";
        int ileOplacalnych = 0;

        try (
                BufferedReader plikTxt = new BufferedReader(new FileReader(nazwaTekstowego));
                RandomAccessFile plikBin = new RandomAccessFile(nazwaBinarnego, "rw");
                BufferedWriter plikRpt = new BufferedWriter(new FileWriter(nazwaRaportu))
        ) {
            String linia;

            while ((linia = plikTxt.readLine()) != null) {
                String[] elementyLinii = linia.split(" ");
                String marka = elementyLinii[0];
                String model = elementyLinii[1];
                double pojemnosc = Double.parseDouble(elementyLinii[2]);
                double akcyza = Double.parseDouble(elementyLinii[3]);
                plikBin.writeDouble(pojemnosc);
                plikBin.writeDouble(akcyza);
                plikRpt.write(marka + " " + model);
                if (pojemnosc > 2000)
                    plikRpt.write(" drogo");
                else {
                    plikRpt.write(" opłacalnie");
                    ++ileOplacalnych;
                }
                plikRpt.newLine();
            }
        } catch (IOException e) {
            return -1;
        }
        return ileOplacalnych;
    }
}