2012-08-07 8 views
7

Ho bisogno di convertire il contenuto di un InputStream in una stringa. La difficoltà qui è la codifica dell'input, vale a dire Latin-1. Ho provato diversi approcci e frammenti di codice con String, getBytes, char [], ecc. Per ottenere la codifica corretta, ma niente sembrava funzionare.Convertire Latin-1 contenuto di InputStream in UTF-8 String

Infine, mi si avvicinò con la soluzione di lavoro di seguito. Tuttavia, questo codice mi sembra un po 'prolisso, anche per Java. Quindi la domanda è:

Esiste un approccio più semplice e più elegante per ottenere ciò che viene fatto qui?

private String convertStreamToStringLatin1(java.io.InputStream is) 
     throws IOException { 

    String text = ""; 

    // setup readers with Latin-1 (ISO 8859-1) encoding 
    BufferedReader i = new BufferedReader(new InputStreamReader(is, "8859_1")); 

    int numBytes; 
    CharBuffer buf = CharBuffer.allocate(512); 
    while ((numBytes = i.read(buf)) != -1) { 
     text += String.copyValueOf(buf.array(), 0, numBytes); 
     buf.clear(); 
    } 

    return text; 
} 

risposta

7

In primo luogo, un paio di critiche l'approccio che hai preso già. Non si dovrebbe usare inutilmente un NIO CharBuffer quando si desidera semplicemente un char[512]. Non è necessario il clear buffer ogni iterazione.

int numBytes; 
final char[] buf = new char[512]; 
while ((numBytes = i.read(buf)) != -1) { 
    text += String.copyValueOf(buf, 0, numBytes); 
} 

Si dovrebbe anche sapere che proprio constructing a String con tali argomenti avrà lo stesso effetto, come il costruttore anche copia i dati.

I contenuti del sottoarray vengono copiati; la successiva modifica dell'array di caratteri non influisce sulla stringa appena creata.


È possibile utilizzare una dinamica ByteArrayOutputStream che cresce un buffer interno per accogliere tutti i dati. È quindi possibile utilizzare l'intero byte[] da toByteArray per la decodifica in un String.

Il vantaggio è che il rinvio decodifica fino alla fine evita frammenti decodifica individualmente; mentre che può funzionare per set di caratteri semplici come ASCII o ISO-8859-1, lo farà non lavoro su schemi multi-byte come UTF-8 e UTF-16. Ciò significa che è più semplice cambiare la codifica dei caratteri in futuro, poiché il codice non richiede alcuna modifica.

private static final String DEFAULT_ENCODING = "ISO-8859-1"; 

public static final String convert(final InputStream in) throws IOException { 
    return convert(in, DEFAULT_ENCODING); 
} 

public static final String convert(final InputStream in, final String encoding) throws IOException { 
    final ByteArrayOutputStream out = new ByteArrayOutputStream(); 
    final byte[] buf = new byte[2048]; 
    int rd; 
    while ((rd = in.read(buf, 0, 2048) >= 0) { 
    out.write(buf, 0, rd); 
    } 
    return new String(out.toByteArray(), 0, encoding); 
} 
+0

Grazie per il tuo commento critico. La tua prima soluzione era come quello che stavo cercando. Tuttavia, posso vedere il tuo punto con la tua seconda soluzione che si rivolge molto al caso generale. Immagino che questo sia anche il motivo per cui la dimensione del buffer è di 2048 byte nel tuo esempio? – cyroxx

+0

Il buffer da 2048 byte era solo una preferenza personale; è possibile utilizzare qualsiasi cosa fornisca un ragionevole compromesso per il consumo di memoria e di runtime. – oldrinb

1

non vedo come potrebbe essere molto più semplice. Ho fatto questo un po 'diverso, una volta .. se si dispone già di una stringa, si può fare questo:

new String(originalString.getBytes(), "ISO-8859-1"); 

Quindi, qualcosa di simile potrebbe anche funzionare:

BufferedReader reader = new BufferedReader(new InputStreamReader(is)); 
StringBuilder sb = new StringBuilder(); 
String line = null; 
while ((line = reader.readLine()) != null) { 
    sb.append(line + "\n"); 
} 
is.close(); 
return new String(sb.toString().getBytes(), "ISO-8859-1"); 

EDIT: devo aggiungere, questo è davvero solo un'alternativa alla tua soluzione già funzionante. Quando si tratta di convertire gli stream in Java, non sarà molto più semplice, quindi fatelo. :)

+0

Ci sono molti miglioramenti qui. In primo luogo, questo non produrrà il testo esatto nel caso in cui non venga trovato alcun terminatore di riga da 'reader.readLine'; aggiungerà un trailing '\ n' che non era originariamente lì. Inoltre, 'BufferedReader' utilizzerà automaticamente la codifica di sistema predefinita. È una buona idea costruire semplicemente ['InputStreamReader'] (http://goo.gl/mhzP1) come se si usasse' StandardCharsets.ISO_8859_1', quindi si può usare 'StringBuilder.toString' in un solo passaggio per acquisire il stringa decodificata correttamente. – oldrinb

+1

Informazioni su \ n: Prendo questo miglioramento grazie, non stavo veramente prestando attenzione alla conversione di InputStream-> String, era solo per completare l'esempio. Il modo diverso di gestire la codifica è ancora ok, ci sono molti modi anche a Roma. ;-) Ma come ho detto è solo un'alternativa. Qualsiasi utilità come commonsIO ripulisce il codice, fa essenzialmente lo stesso e dipende da una libreria aggiuntiva. Ha senso se la usi più spesso .. una questione di scelta personale. – Blacklight

0

Se non si vuole scandagliare da soli si potrebbe avere uno sguardo ai beni comuni apache io progetto, IOUtils.toString(InputStream input, String encoding) che sembra fare quello che vuoi. Non ho provato questo metodo da solo, ma il documento java afferma "Ottieni il contenuto di un InputStream come una stringa usando la codifica di caratteri specificata."

0

Il pacchetto di I/O Guava è davvero carino in questo modo.

Files.toString(yourFile, CharSets.ISO_8859_1) 

o da un flusso

new String(ByteStreams.toByteArray(stream), CharSets.ISO_8859_1) 
0

ho appena scoperto che this answer alla domanda Read/convert an InputStream to a String può essere applicato al mio problema, vedere il codice qui sotto. Ad ogni modo, apprezzo molto le risposte che hai dato finora.

private String convertStreamToString(InputStream is, String charsetName) { 
    try { 
     return new java.util.Scanner(is, charsetName).useDelimiter("\\A").next(); 
    } catch (java.util.NoSuchElementException e) { 
     return ""; 
    } 
} 

Quindi, al fine di codificare dal latino-1, chiamare in questo modo:

String message = convertStreamToString(is, "8859_1"); 
+0

Dovresti sapere che 'Scanner' compila internamente una espressione' Pattern' per il delimitatore. Questo metodo è davvero interessante e ingegnoso, ma probabilmente anche non consigliabile. – oldrinb

+0

Vorrei approfondire questo aspetto: qual è il problema con questo modello? Non dovrebbe essere piuttosto leggero? – cyroxx

+0

Sembra solo una soluzione interessante ma un abuso di 'Scanner'. Nella risposta a cui ti sei collegato, lo hanno messo bene ... un * stupido 'trucco Scanner' *. – oldrinb

Problemi correlati