Problema di codifica Java FileReader


130

Ho provato a usare java.io.FileReader per leggere alcuni file di testo e convertirli in una stringa, ma ho scoperto che il risultato è codificato erroneamente e non è affatto leggibile.

Ecco il mio ambiente:

  • Windows 2003, codifica del sistema operativo: CP1252

  • Java 5.0

I miei file sono codificati UTF-8 o codificati CP1252 e alcuni di essi (file codificati UTF-8) possono contenere caratteri cinesi (non latini).

Uso il seguente codice per svolgere il mio lavoro:

   private static String readFileAsString(String filePath)
    throws java.io.IOException{
        StringBuffer fileData = new StringBuffer(1000);
        FileReader reader = new FileReader(filePath);
        //System.out.println(reader.getEncoding());
        BufferedReader reader = new BufferedReader(reader);
        char[] buf = new char[1024];
        int numRead=0;
        while((numRead=reader.read(buf)) != -1){
            String readData = String.valueOf(buf, 0, numRead);
            fileData.append(readData);
            buf = new char[1024];
        }
        reader.close();
        return fileData.toString();
    }

Il codice sopra non funziona. Ho trovato che la codifica del FileReader è CP1252 anche se il testo è codificato UTF-8. Ma JavaDoc di java.io.FileReader dice che:

I costruttori di questa classe presumono che la codifica dei caratteri predefinita e la dimensione del buffer byte predefinita siano appropriate.

Questo significa che non devo impostare la codifica dei caratteri da solo se sto usando FileReader? Ma al momento ho ricevuto dati codificati erroneamente, qual è il modo corretto di gestire la mia situtaion? Grazie.


Dovresti anche perdere String.valueOf () all'interno del ciclo e utilizzare direttamente StringBuffer.append (char [], int, int). Ciò consente di risparmiare molte copie del carattere []. Sostituisci anche StringBuffer con StringBuilder. Niente di tutto questo riguarda la tua domanda ", comunque.
Joachim Sauer,

1
Odio dirlo, ma hai letto JavaDoc subito dopo la parte che hai incollato? Sai, la parte che dice "Per specificare questi valori tu stesso, costruisci un InputStreamReader su un FileInputStream."?
Powerlord,

Grazie per il tuo commento, in realtà ho letto JavaDoc, ma quello che non sono sicuro è se dovrei specificare questi valori da solo e passare a "costruire un InputStreamReader su un FileInputStream".
Nybon,

Sì, se sai che il file si trova in qualcosa di diverso dalla codifica predefinita della piattaforma, devi dire a InputStreamReader quale utilizzare.
Alan Moore,

Risposte:


248

Sì, è necessario specificare la codifica del file che si desidera leggere.

Sì, questo significa che devi conoscere la codifica del file che vuoi leggere.

No, non esiste un modo generale per indovinare la codifica di un determinato file "testo normale".

I costruttori a argomento singoloFileReader usano sempre la codifica predefinita della piattaforma che è generalmente una cattiva idea .

Da quando Java 11 FileReaderha anche acquisito costruttori che accettano una codifica: new FileReader(file, charset)e new FileReader(fileName, charset).

Nelle versioni precedenti di java, è necessario utilizzare .new InputStreamReader(new FileInputStream(pathToFile), <encoding>)


1
InputStream è = new FileInputStream (nome file); qui ho trovato il file di errore non trovato errore con il nome del file russo
Bhanu Sharma

3
+1 per il suggerimento di utilizzare InputStreamReader, tuttavia l'uso di collegamenti in blocchi di codice rende difficile copiare e incollare il codice, se questo può essere modificato, grazie
Ferrybig

1
Sarebbe "UTF-8" o "UTF8" nelle codifiche. Secondo il riferimento Java SE sulla codifica , poiché InputStreamReaderè una java.ioclasse, sarebbe "UTF8"?
NobleUplift,

9
@NobleUplift: la scommessa più sicura è che StandardCharsets.UTF_8non c'è possibilità di errori di digitazione lì ;-) Ma sì, se vai con lo spago "UTF8"sarebbe corretto (anche se mi sembra di ricordare che accetterà in entrambi i modi).
Joachim Sauer,

1
@JoachimSauer In realtà, questo è uno degli scopi del Byte Order Mark, insieme a ... beh .. stabilire l'ordine dei byte! :) In quanto tale trovo strano che il FileReader di Java non sia in grado di rilevare automaticamente UTF-16 che ha una tale distinta base ... In effetti una volta ho scritto un UnicodeFileReaderche fa esattamente questo. Purtroppo è chiuso, ma Google ha UnicodeReader che è molto simile.
Stijn de Witt,

79

FileReader utilizza la codifica predefinita della piattaforma Java, che dipende dalle impostazioni di sistema del computer su cui è in esecuzione ed è generalmente la codifica più popolare tra gli utenti in quella locale.

Se questa "ipotesi migliore" non è corretta, è necessario specificare esplicitamente la codifica. Sfortunatamente, FileReaderciò non lo consente (svista importante nell'API). Invece, devi usare new InputStreamReader(new FileInputStream(filePath), encoding)e idealmente ottenere la codifica dai metadati sul file.


24
"grande svista nell'API" - grazie per questa spiegazione - mi chiedevo perché non riuscivo a trovare il costruttore che cercavo! Saluti John
monojohnny,

@Bhanu Sharma: questo è un problema di codifica a un livello diverso, controlla da dove stai ottenendo il nome file e se è codificato quale codifica utilizza il compilatore.
Michael Borgwardt,

1
@BhanuSharma: i problemi di codifica del nome file non hanno nulla a che fare con questa domanda. Guarda una delle molte domande esistenti sul perché i nomi dei file Unicode non funzionano in Java? Spoiler: API java.io come FileReader utilizzano chiamate al filesystem della libreria standard C, che non possono supportare Unicode su Windows; considera invece l'utilizzo di java.nio.
Bobince,

1
" FileReaderutilizza la codifica predefinita della piattaforma Java, che dipende dalle impostazioni di sistema del computer su cui è in esecuzione ed è generalmente la codifica più popolare tra gli utenti in quella locale." Non lo direi. Almeno di Windows. Per alcuni strani motivi tecnici / storici, JVM ignora il fatto che Unicode è la codifica consigliata su Windows per "tutte le nuove applicazioni" e agisce invece sempre come se la codifica legacy configurata come fallback per le app legacy sia il "predefinito di piattaforma".
Stijn de Witt,

6
Vorrei anche dire che se la tua app Java non specifica esplicitamente le codifiche ogni volta che legge o scrive su file / flussi / risorse, è rotta , perché non potrà mai funzionare in modo affidabile.
Stijn de Witt,

8

Da Java 11 puoi usare quello:

public FileReader(String fileName, Charset charset) throws IOException;

6

Per il documento Java 7+ puoi usare questo:

BufferedReader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8);

Ecco tutti i Charsets doc

Ad esempio, se il file è in CP1252, utilizzare questo metodo

Charset.forName("windows-1252");

Ecco altri nomi canonici per le codifiche Java sia per IO che per NIO doc

Se non si conosce esattamente la codifica che avete ottenuto in un file, è possibile utilizzare alcune librerie di terze parti come questo strumento da parte di Google questo che funziona abbastanza ordinata.


1

FileInputStream con InputStreamReader è meglio che utilizzare direttamente FileReader, poiché quest'ultimo non consente di specificare il set di caratteri di codifica.

Ecco un esempio usando BufferedReader, FileInputStream e InputStreamReader insieme, in modo da poter leggere le righe da un file.

List<String> words = new ArrayList<>();
List<String> meanings = new ArrayList<>();
public void readAll( ) throws IOException{
    String fileName = "College_Grade4.txt";
    String charset = "UTF-8";
    BufferedReader reader = new BufferedReader(
        new InputStreamReader(
            new FileInputStream(fileName), charset)); 

    String line; 
    while ((line = reader.readLine()) != null) { 
        line = line.trim();
        if( line.length() == 0 ) continue;
        int idx = line.indexOf("\t");
        words.add( line.substring(0, idx ));
        meanings.add( line.substring(idx+1));
    } 
    reader.close();
}

0

Per un altro come le lingue latine, ad esempio in cirillico, puoi usare qualcosa del genere:

FileReader fr = new FileReader("src/text.txt", StandardCharsets.UTF_8);

e assicurati che il tuo .txtfile sia salvato con il formato UTF-8(ma non come predefinito ANSI). Saluti!

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.