2009-01-31 15 views
6

Ho un file codificato come iso-8859-1 e contiene caratteri come ô.Java App: impossibile leggere correttamente il file codificato iso-8859-1

io sto leggendo questo file con il codice Java, qualcosa di simile:

File in = new File("myfile.csv"); 
InputStream fr = new FileInputStream(in); 
byte[] buffer = new byte[4096]; 
while (true) { 
    int byteCount = fr.read(buffer, 0, buffer.length); 
    if (byteCount <= 0) { 
     break; 
    } 

    String s = new String(buffer, 0, byteCount,"ISO-8859-1"); 
    System.out.println(s); 
} 

Tuttavia il carattere O è sempre confuso, di solito di stampa come una? .

Ho letto l'argomento (e ho imparato un po 'sulla strada) ad es.

ma ancora non è possibile ottenere questo lavoro

È interessante notare che questo funziona sul mio pc locale (xp), ma non sulla mia macchina Linux.

Ho verificato che il mio JDK supporta i set di caratteri richiesti (sono di serie, quindi questo è senza sorpresa) utilizzando:

System.out.println(java.nio.charset.Charset.availableCharsets()); 
+0

Dovrei aggiungere che sono in grado di vedere correttamente i caratteri o il file originale usando il mio terminale Linux se semplicemente cat i contenuti del file – Joel

+0

Quale codifica dei caratteri viene utilizzata dal terminale? – McDowell

+0

È interessante notare che se aggiungo la proprietà java di runtime "-Dfile.encoding = UTF16" funziona come previsto, anche se non vedo perché questo dovrebbe essere importante - e non lo vedo come una soluzione, ma piuttosto come un trucco. Non funziona con la proprietà impostata su UTF8. – Joel

risposta

12

Ho il sospetto che sia il file non è in realtà codificato come ISO-8859-1, o System.out non sa come stampare il carattere.

Si consiglia di controllare per primo il byte pertinente nel file. Per controllare il secondo, esaminare il relativo carattere nella stringa, stampandolo con

System.out.println((int) s.getCharAt(index)); 

In entrambi i casi il risultato dovrebbe essere 244 decimale; 0xf4 esadecimale.

Vedere my article on Unicode debugging per un consiglio generale (il codice presentato è in C#, ma è facile convertirlo in Java e i principi sono gli stessi).

In generale, a proposito, avrei avvolto il flusso con un InputStreamReader con la giusta codifica: è più facile che creare nuove stringhe "a mano". Mi rendo conto che questo potrebbe essere solo il codice demo però.

EDIT: Ecco un modo molto semplice per dimostrare se la console funzionerà:

System.out.println("Here's the character: \u00f4"); 
+0

hanno utilizzato lo strumento file linux per testare il tipo di file: file --mime FranceJ2.csv FranceJ2.csv: text/plain; charset = iso-8859-1 e anche confermato che posso leggerlo correttamente, in dire vi ma seguirò i vostri suggerimenti. – Joel

+1

Non fidatevi degli strumenti che tentano di rilevare automaticamente le codifiche dei caratteri. Sono sempre basati solo sull'euristica e devono essere. Non sanno quale testo il tuo file è in realtà destinato a contenere. –

+0

Un dump esadecimale del file produce: 0000000 0df4 000a (qualche suggerimento !?) – Joel

3

Se è possibile, provare a eseguire il programma in debugger per vedere cosa c'è dentro il vostro 's' una stringa dopo che è stata creata. È possibile che abbia contenuto corretto, ma l'output è confuso dopo la chiamata di System.out.println (s). In questo caso, probabilmente c'è una discrepanza tra ciò che Java pensa sia la codifica dell'output e la codifica dei caratteri del tuo terminale/console su Linux.

9

l'analisi del file come blocchi di dimensione fissa di byte non è buono --- cosa se qualche personaggio ha una rappresentazione di byte che si trova a cavallo tra due blocchi? Utilizzare un InputStreamReader con la codifica dei caratteri appropriata invece:

BufferedReader br = new BufferedReader(
     new InputStreamReader(
     new FileInputStream("myfile.csv"), "ISO-8859-1"); 

char[] buffer = new char[4096]; // character (not byte) buffer 

while (true) 
{ 
     int charCount = br.read(buffer, 0, buffer.length); 

     if (charCount == -1) break; // reached end-of-stream 

     String s = String.valueOf(buffer, 0, charCount); 
     // alternatively, we can append to a StringBuilder 

     System.out.println(s); 
} 

Btw, ricordatevi di controllare che il carattere unicode può infatti essere visualizzato correttamente.È inoltre possibile reindirizzare l'output del programma su un file e quindi confrontarlo con il file originale.

Come suggerisce Jon Skeet, il problema potrebbe anche essere relativo alla console. Prova System.console().printf(s) per vedere se c'è una differenza.

1

Fondamentalmente, se funziona sul tuo PC XP locale ma non su Linux, e stai analizzando lo stesso file esatto (cioè lo hai trasferito in modo binario tra le caselle), allora probabilmente ha qualcosa a che fare con il Chiamata System.out.println. Non so come si verifica l'output, ma se lo si fa connettendosi con una shell remota dalla casella XP, quindi c'è il set di caratteri della shell (e del client) da considerare.

Inoltre, ciò che Zach Scrivena suggerisce è anche vero - non si può presumere che sia possibile creare stringhe da blocchi di dati in questo modo - utilizzare un InputStreamReader o leggere prima i dati completi in un array (ovviamente non funzionerà per un file di grandi dimensioni). Tuttavia, dal momento che sembra funzionare su XP, quindi mi azzarderei che questo non è probabilmente il tuo problema in questo caso specifico.

6

@Joel - your own answer conferma che il problema è una differenza tra la codifica predefinita sul sistema operativo (UTF-8, quella rilevata da Java) e la codifica utilizzata dal terminale (ISO-8859-1).

Considerate questo codice:

public static void main(String[] args) throws IOException { 
    byte[] data = { (byte) 0xF4 }; 
    String decoded = new String(data, "ISO-8859-1"); 
    if (!"\u00f4".equals(decoded)) { 
     throw new IllegalStateException(); 
    } 

    // write default charset 
    System.out.println(Charset.defaultCharset()); 

    // dump bytes to stdout 
    System.out.write(data); 

    // will encode to default charset when converting to bytes 
    System.out.println(decoded); 
} 

Per impostazione predefinita, il mio Ubuntu (8.04) terminale utilizza la codifica UTF-8. Con questa codifica, questo viene stampato:?

UTF-8
& # x00F4;

Se codificatore del terminale ISO 8859-1, questo viene stampato:

UTF-8
& # x00F4; & # x00C3; & # x00B4;

In entrambi i casi, gli stessi byte vengono emessi dal programma Java:

5554 462d 380a f4c3 b40a 

L'unica differenza è nel modo in cui il terminale è interpretare i byte ricevuti. In ISO 8859-1, & # x00F4; è codificato come 0xF4. In UTF-8, & # x00F4; è codificato come 0xC3B4. Gli altri caratteri sono comuni a entrambe le codifiche.

+0

Mi manca sicuramente qualcosa qui - qual è la discarica '5554 462d 380a f4c3 b40a'? Certamente non la chiamata 'System.out.write (data)'? –

+1

@Mr_and_Mrs_D Questi sono i byte scritti da JRE sul dispositivo (STDOUT) con tutte e tre le chiamate su 'System.out'. I byte '0A' segnano le nuove righe scritte da' println'. C'era una risposta scritta dall'autore della domanda, poiché cancellata, ma non credo che riuscire a leggerla aggiunga molto._ – McDowell

+0

Grazie per il seguito - ho capito che c'era una risposta dall'autore dopo l'eliminazione - non posso leggerlo - Grazie :) –