1. Introduzione
Diciamolo subito: se almeno una volta avete visto al posto del testo in russo qualcosa come Привет, siete già vittime di una codifica errata. Succede quando un file è stato scritto con una codifica e viene letto con un'altra. Per esempio, il file è stato salvato come UTF-8 ma viene letto come Windows-1251, o viceversa.
Java per impostazione predefinita usa la codifica di sistema, che si può ottenere così:
System.out.println(System.getProperty("file.encoding"));
Su un computer può essere UTF-8, su un altro — Windows-1251, e altrove — ISO-8859-1. Per questo motivo è sempre meglio specificare esplicitamente la codifica. È particolarmente importante se lavorate con dati multilingue, se i file verranno usati su computer diversi o aperti in altri programmi, o se volete che il vostro codice si comporti allo stesso modo in qualsiasi ambiente, e non solo sulla vostra macchina.
La classe Charset: il vostro alleato nel mondo delle codifiche
In Java per lavorare con le codifiche si usa la classe java.nio.charset.Charset. Permette di specificare la codifica per nome (ad esempio, "UTF-8") oppure di usare le costanti standard (StandardCharsets.UTF_8).
Esempi di codifiche standard:
| Codifica | Costante Java |
|---|---|
| UTF-8 | |
| UTF-16 | |
| ISO-8859-1 | |
| Windows-1251 | |
È preferibile usare le costanti: meno probabilità di sbagliare il nome e non si verificherà UnsupportedCharsetException.
2. Lettura dei file con specifica della codifica
Metodo vecchio:
import java.io.*;
import java.nio.charset.StandardCharsets;
BufferedReader reader = new BufferedReader(
new InputStreamReader(
new FileInputStream("example.txt"),
StandardCharsets.UTF_8 // <-- Specifichiamo esplicitamente la codifica
)
);
Metodo moderno:
import java.nio.file.*;
import java.nio.charset.StandardCharsets;
import java.io.BufferedReader;
BufferedReader reader = Files.newBufferedReader(
Paths.get("example.txt"),
StandardCharsets.UTF_8 // <-- Specifichiamo esplicitamente la codifica
);
Si consiglia di usare il secondo approccio — è più conciso, più sicuro e si integra bene con try-with-resources.
Esempio: leggiamo una riga da un file
try (BufferedReader reader = Files.newBufferedReader(
Paths.get("hello.txt"),
StandardCharsets.UTF_8)) {
String line = reader.readLine();
System.out.println("Letto: " + line);
}
Perché è importante: Se il file è stato salvato in UTF-8 ma lo leggete come Windows-1251, i caratteri cirillici verranno distorti. Specificando la codifica corretta, il testo verrà letto correttamente su qualsiasi sistema operativo.
3. Scrittura dei file con specifica della codifica
Metodo vecchio:
import java.io.*;
import java.nio.charset.StandardCharsets;
BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream("example.txt"),
StandardCharsets.UTF_8 // <-- Specifichiamo esplicitamente la codifica
)
);
Metodo moderno:
import java.nio.file.*;
import java.nio.charset.StandardCharsets;
import java.io.BufferedWriter;
BufferedWriter writer = Files.newBufferedWriter(
Paths.get("example.txt"),
StandardCharsets.UTF_8 // <-- Specifichiamo esplicitamente la codifica
);
Esempio: scriviamo una riga in un file
try (BufferedWriter writer = Files.newBufferedWriter(
Paths.get("hello.txt"),
StandardCharsets.UTF_8)) {
writer.write("Ciao, mondo!");
}
Risultato: Il file verrà salvato in UTF-8 e potrà essere aperto correttamente in qualsiasi editor che supporti UTF-8.
4. Dettagli utili
Come scoprire le codifiche supportate
import java.nio.charset.Charset;
public class ListCharsets {
public static void main(String[] args) {
System.out.println("Codifiche disponibili:");
Charset.availableCharsets().forEach((name, charset) -> System.out.println(name));
}
}
Consiglio: Se usate una codifica esotica (ad esempio per ideogrammi cinesi antichi o per faccine marziane), verificate che sia supportata dalla vostra JVM.
Uso di try-with-resources: non dimenticare di chiudere gli stream
Quando si lavora con i file, è importante chiudere gli stream per evitare perdite di risorse. Il codice Java moderno usa la costruzione try-with-resources:
try (BufferedReader reader = Files.newBufferedReader(path, charset)) {
// Lavoriamo con il file
}
Lo stream verrà chiuso automaticamente, anche se si verifica un errore.
Raccomandazioni
- Meglio specificare sempre esplicitamente la codifica durante la lettura e la scrittura dei file, anche se siete sicuri che «di default funziona tutto».
- Usate UTF-8 per i nuovi file — è lo standard de facto, soprattutto se lavorate con il web, JSON, XML, o se volete che i vostri file siano leggibili ovunque.
- Per i file legacy (ad esempio, esportazioni da 1C, vecchi database, CSV da Windows) usate la codifica con cui sono stati salvati (ad esempio, Windows-1251, ISO-8859-1).
- Non usate classi obsolete, in cui la codifica non è specificata esplicitamente: FileReader/FileWriter. Al loro posto utilizzate InputStreamReader/OutputStreamWriter con codifica esplicita oppure i metodi di Files.
- Per file grandi usate la bufferizzazione (BufferedReader/BufferedWriter) per non esaurire tutta la memoria.
5. Errori tipici nella gestione delle codifiche
Errore n. 1: codifica non specificata durante la lettura/scrittura del file.
Se non si specifica la codifica, Java usa quella di sistema predefinita ("file.encoding"). Sulla vostra macchina tutto funziona, ma su quella del collega — caratteri illeggibili.
Errore n. 2: mancata corrispondenza tra codifiche in lettura e scrittura.
Il file è stato scritto con una codifica, ma viene letto con un'altra. Ad esempio, il file è scritto in UTF-8 ma viene letto come Windows-1251 — i caratteri cirillici risultano alterati.
Errore n. 3: uso delle classi obsolete FileReader/FileWriter.
Queste classi non permettono di specificare esplicitamente la codifica — il loro uso è sconsigliato. Al loro posto usate InputStreamReader/OutputStreamWriter indicando la codifica oppure i metodi di Files.
Errore n. 4: errore nel nome della codifica.
Per esempio, avete scritto "utf8" invece di "UTF-8" o "win1251" invece di "Windows-1251". Java genererà UnsupportedCharsetException.
Errore n. 5: stream non chiuso — file non scritto.
Se non si usa try-with-resources o non si chiude esplicitamente lo stream, una parte dei dati potrebbe non essere scritta su disco.
GO TO FULL VERSION