CodeGym /Kursy /JAVA 25 SELF /Ustalanie kodowania przy odczycie/zapisie plików

Ustalanie kodowania przy odczycie/zapisie plików

JAVA 25 SELF
Poziom 37 , Lekcja 2
Dostępny

1. Wprowadzenie

Powiedzmy to wprost: jeśli choć raz widzieliście zamiast rosyjskiego tekstu coś w rodzaju Привет, padliście ofiarą niewłaściwego kodowania. Dzieje się tak, gdy plik został zapisany w jednym kodowaniu, a odczytywany w innym. Na przykład plik zapisano jako UTF-8, a czytany jest jako Windows-1251, lub odwrotnie.

Java domyślnie używa systemowego kodowania, które można sprawdzić tak:

System.out.println(System.getProperty("file.encoding"));

Na jednym komputerze może to być UTF-8, na innym — Windows-1251, a gdzie indziej — ISO-8859-1. Dlatego zawsze lepiej wskazywać kodowanie wprost. Jest to szczególnie ważne, jeśli pracujesz z danymi wielojęzycznymi, jeśli pliki będą używane na różnych komputerach lub otwierane w innych programach, albo jeśli chcesz, by twój kod zachowywał się tak samo w każdym środowisku, a nie tylko na twojej maszynie.

Klasa Charset: twój przyjaciel w świecie kodowań

W Javie do pracy z kodowaniami służy klasa java.nio.charset.Charset. Pozwala wskazać kodowanie po nazwie (na przykład "UTF-8") oraz używać standardowych stałych (StandardCharsets.UTF_8).

Przykłady standardowych kodowań:

Kodowanie Stała w Javie
UTF-8
StandardCharsets.UTF_8
UTF-16
StandardCharsets.UTF_16
ISO-8859-1
StandardCharsets.ISO_8859_1
Windows-1251
Charset.forName("Windows-1251")

Używanie stałych jest preferowane: mniejsze ryzyko pomyłki w nazwie i nie wystąpi UnsupportedCharsetException.

2. Odczyt plików z podaniem kodowania

Stary sposób:

import java.io.*;
import java.nio.charset.StandardCharsets;

BufferedReader reader = new BufferedReader(
    new InputStreamReader(
        new FileInputStream("example.txt"),
        StandardCharsets.UTF_8 // <-- Jawnie podajemy kodowanie
    )
);

Nowoczesny sposób:

import java.nio.file.*;
import java.nio.charset.StandardCharsets;
import java.io.BufferedReader;

BufferedReader reader = Files.newBufferedReader(
    Paths.get("example.txt"),
    StandardCharsets.UTF_8 // <-- Jawnie podajemy kodowanie
);

Zaleca się używać drugiego sposobu — jest krótszy, bezpieczniejszy i wygodnie łączy się z try-with-resources.

Przykład: czytamy wiersz z pliku

try (BufferedReader reader = Files.newBufferedReader(
        Paths.get("hello.txt"),
        StandardCharsets.UTF_8)) {
    String line = reader.readLine();
    System.out.println("Przeczytano: " + line);
}

Dlaczego to ważne: Jeśli plik został zapisany w UTF-8, a czytasz go jako Windows-1251, znaki cyryliczne zostaną zniekształcone. Jeśli podasz właściwe kodowanie, tekst będzie odczytywany poprawnie na dowolnym systemie operacyjnym.

3. Zapis plików z podaniem kodowania

Stary sposób:

import java.io.*;
import java.nio.charset.StandardCharsets;

BufferedWriter writer = new BufferedWriter(
    new OutputStreamWriter(
        new FileOutputStream("example.txt"),
        StandardCharsets.UTF_8 // <-- Jawnie podajemy kodowanie
    )
);

Nowoczesny sposób:

import java.nio.file.*;
import java.nio.charset.StandardCharsets;
import java.io.BufferedWriter;

BufferedWriter writer = Files.newBufferedWriter(
    Paths.get("example.txt"),
    StandardCharsets.UTF_8 // <-- Jawnie podajemy kodowanie
);

Przykład: zapisujemy wiersz do pliku

try (BufferedWriter writer = Files.newBufferedWriter(
        Paths.get("hello.txt"),
        StandardCharsets.UTF_8)) {
    writer.write("Witaj, świecie!");
}

Wynik: Plik zostanie zapisany w UTF-8 i będzie można go poprawnie otworzyć w każdym edytorze obsługującym UTF-8.

4. Przydatne szczegóły

Jak sprawdzić obsługiwane kodowania

import java.nio.charset.Charset;

public class ListCharsets {
    public static void main(String[] args) {
        System.out.println("Dostępne kodowania:");
        Charset.availableCharsets().forEach((name, charset) -> System.out.println(name));
    }
}

Wskazówka: Jeśli używasz egzotycznego kodowania (na przykład dla starożytnych chińskich znaków lub marsjańskich emotikonów), sprawdź, czy jest ono obsługiwane przez twoją JVM.

Użycie try-with-resources: nie zapominaj o zamykaniu strumieni

Pracując z plikami, ważne jest zamykanie strumieni, aby nie było wycieków zasobów. Nowoczesny kod Java używa konstrukcji try-with-resources:

try (BufferedReader reader = Files.newBufferedReader(path, charset)) {
    // Pracujemy z plikiem
}

Strumień zamknie się automatycznie, nawet jeśli wystąpi błąd.

Zalecenia

  • Lepiej zawsze jawnie podawać kodowanie przy odczycie i zapisie plików, nawet jeśli jesteś pewien, że „domyślnie wszystko działa”.
  • Używaj UTF-8 dla nowych plików — to de facto standard, zwłaszcza jeśli pracujesz z web, JSON, XML, lub chcesz, aby twoje pliki były czytelne wszędzie.
  • Dla starych plików (np. eksport z 1C, starych baz danych, CSV z Windows) używaj tego kodowania, w którym zostały zapisane (na przykład Windows-1251, ISO-8859-1).
  • Nie używaj przestarzałych klas, w których nie podaje się kodowania wprost: FileReader/FileWriter. Zamiast nich stosuj InputStreamReader/OutputStreamWriter z jawnym kodowaniem lub metody z Files.
  • Dla dużych plików używaj buforowania (BufferedReader/BufferedWriter), żeby nie zająć całej pamięci.

5. Typowe błędy przy pracy z kodowaniami

Błąd nr 1: Nie podano kodowania przy odczycie/zapisie pliku.
Jeśli nie podasz kodowania, Java użyje systemowego domyślnego ("file.encoding"). Na twojej maszynie wszystko działa, a u kolegi — „krzaczki”.

Błąd nr 2: Niezgodność kodowań przy odczycie i zapisie.
Plik został zapisany w jednym kodowaniu, a czytany w innym. Na przykład plik zapisany w UTF-8, a odczytywany jako Windows-1251 — cyrylica zostaje zniekształcona.

Błąd nr 3: Używanie przestarzałych klas FileReader/FileWriter.
Te klasy nie pozwalają jawnie wskazać kodowania — nie zaleca się ich używania. Zamiast nich używaj InputStreamReader/OutputStreamWriter z podaniem kodowania albo metod z Files.

Błąd nr 4: Błąd w nazwie kodowania.
Na przykład napisano "utf8" zamiast "UTF-8" lub "win1251" zamiast "Windows-1251". Java rzuci UnsupportedCharsetException.

Błąd nr 5: Strumień nie został zamknięty — plik nie został zapisany.
Jeśli nie użyjesz try-with-resources lub jawnie nie zamkniesz strumienia, część danych może nie trafić na dysk.

Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION