Odczyt i zapis do plików w Javie

Manipulacja plikami jest jednym z podstawowych zagadnień programowania w Javie. Niezależnie od tego, czy chcesz zapisywać dane aplikacji, generować logi, czy obsługiwać zewnętrzne pliki konfiguracyjne, umiejętność pracy z plikami jest nieodzowna. W tym artykule przyjrzymy się różnych sposoby odczytu i zapisu plików w Javie, z uwzględnieniem najlepszych praktyk i popularnych bibliotek.


1. Podstawy obsługi plików w Javie

Java udostępnia bogaty zestaw narzędzi do obsługi plików. W standardowej bibliotece znajdziemy klasy takie jak:

  • File – do manipulacji ścieżkami i informacją o plikach.
  • FileReader/FileWriter – do prostego odczytu i zapisu tekstu.
  • BufferedReader/BufferedWriter – do wydajniejszej obsługi strumieni tekstowych.
  • Files – klasa z narzędziami do odczytu, zapisu i kopiowania plików.
  • Scanner – do wygodnego odczytu danych z plików tekstowych.

2. Tworzenie i zarządzanie plikami

Klasa File umożliwia sprawdzanie istnienia pliku, tworzenie nowych plików oraz uzyskiwanie informacji o plikach.

Przykład 1: Tworzenie pliku

import java.io.File;
import java.io.IOException;

public class FileExample {
    public static void main(String[] args) {
        File file = new File("example.txt");

        try {
            if (file.createNewFile()) {
                System.out.println("Plik utworzony: " + file.getName());
            } else {
                System.out.println("Plik już istnieje.");
            }
        } catch (IOException e) {
            System.out.println("Wystąpił błąd.");
            e.printStackTrace();
        }
    }
}

W powyższym przykładzie metoda createNewFile() tworzy plik, jeśli nie istnieje, a jeśli plik już istnieje – nic nie robi.

Przykład 2: Sprawdzanie informacji o pliku

File file = new File("example.txt");
if (file.exists()) {
    System.out.println("Nazwa pliku: " + file.getName());
    System.out.println("Ścieżka absolutna: " + file.getAbsolutePath());
    System.out.println("Czy plik jest zapisywalny: " + file.canWrite());
    System.out.println("Czy plik jest odczytywalny: " + file.canRead());
    System.out.println("Rozmiar pliku: " + file.length() + " bajtów");
}

3. Odczyt danych z plików

Użycie klasy FileReader i BufferedReader

FileReader i BufferedReader pozwalają odczytywać dane tekstowe w liniach lub znakach. BufferedReader jest bardziej wydajny, gdyż odczytuje dane w buforach.

Przykład: Odczyt całego pliku tekstowego

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class ReadFileExample {
    public static void main(String[] args) {
        try (BufferedReader br = new BufferedReader(new FileReader("example.txt"))) {
            String line;
            while ((line = br.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Użycie klasy Scanner

Scanner pozwala wygodnie odczytywać dane, zwłaszcza gdy potrzebujemy rozdzielania wierszy lub danych według separatorów.

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;

public class ScannerExample {
    public static void main(String[] args) {
        try (Scanner scanner = new Scanner(new File("example.txt"))) {
            while (scanner.hasNextLine()) {
                System.out.println(scanner.nextLine());
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }
}

4. Zapis danych do plików

Użycie FileWriter i BufferedWriter

FileWriter i BufferedWriter służą do zapisywania tekstu do plików. BufferedWriter poprawia wydajność przez zapisywanie danych w buforach.

Przykład: Zapis danych do pliku

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;

public class WriteFileExample {
    public static void main(String[] args) {
        try (BufferedWriter bw = new BufferedWriter(new FileWriter("example.txt", true))) {
            bw.write("Dodajemy nową linię do pliku.");
            bw.newLine();
            bw.write("To jest kolejny wiersz.");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

W tym kodzie użyto opcji true w konstruktorze FileWriter, co oznacza, że dane będą dopisywane na końcu pliku.


5. Praca z plikami binarnymi

Java oferuje klasy FileInputStream i FileOutputStream do obsługi plików binarnych.

Przykład: Kopiowanie pliku binarnego

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class BinaryFileExample {
    public static void main(String[] args) {
        try (FileInputStream fis = new FileInputStream("source.bin");
             FileOutputStream fos = new FileOutputStream("destination.bin")) {

            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = fis.read(buffer)) != -1) {
                fos.write(buffer, 0, bytesRead);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

6. Użycie klasy Files z pakietu java.nio

Klasa Files oferuje nowoczesne i proste API do pracy z plikami. Możemy jej użyć do odczytu, zapisu, kopiowania, usuwania plików i wielu innych operacji.

Przykład: Odczyt całego pliku jako lista wierszy

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;

public class NIOExample {
    public static void main(String[] args) {
        try {
            Path path = Path.of("example.txt");
            List<String> lines = Files.readAllLines(path);
            lines.forEach(System.out::println);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Przykład: Zapis tekstu do pliku

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;

public class WriteNIOExample {
    public static void main(String[] args) {
        try {
            Path path = Path.of("example.txt");
            List<String> lines = List.of("Pierwsza linia", "Druga linia", "Trzecia linia");
            Files.write(path, lines);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

7. Obsługa wyjątków i najlepsze praktyki

  • Używaj try-with-resources: Automatycznie zamyka zasoby, takie jak strumienie plików.
  • Waliduj dane wejściowe: Sprawdzaj, czy ścieżka do pliku jest poprawna.
  • Zachowuj bezpieczeństwo danych: W przypadku operacji krytycznych stosuj kopie zapasowe plików.
  • Loguj błędy: Przydatne przy debugowaniu.

8. Wydajność i optymalizacja

  • BufferedReader/BufferedWriter: Używaj buforów do pracy z tekstem.
  • Strumienie binarne: Dla dużych plików binarnych.
  • Java NIO: Do pracy z dużymi plikami i nowoczesnymi systemami plików.
  • Wielowątkowość: Rozważ wielowątkowe operacje dla złożonych zadań.

Praca z plikami w Javie oferuje szerokie możliwości, a zrozumienie podstawowych i zaawansowanych technik pozwala tworzyć efektywne aplikacje. Obsługa odczytu, zapisu oraz przetwarzania danych to umiejętność, która z pewnością przyda się w każdym projekcie.