Impostazione della codifica dei caratteri Java predefinita


362

Come posso impostare correttamente la codifica dei caratteri predefinita utilizzata dalla JVM (1.5.x) a livello di codice?

Ho letto che -Dfile.encoding=whateverera la strada da percorrere per le JVM più vecchie. Non ho quel lusso per ragioni che non mi interessano.

Ho provato:

System.setProperty("file.encoding", "UTF-8");

E la proprietà viene impostata, ma non sembra che l'ultima getByteschiamata in basso usi UTF8:

System.setProperty("file.encoding", "UTF-8");

byte inbytes[] = new byte[1024];

FileInputStream fis = new FileInputStream("response.txt");
fis.read(inbytes);
FileOutputStream fos = new FileOutputStream("response-2.txt");
String in = new String(inbytes, "UTF8");
fos.write(in.getBytes());

Ottimi commenti ragazzi - e cose a cui stavo già pensando. Sfortunatamente c'è una chiamata String.getBytes () su cui non ho alcun controllo. L'unico modo che attualmente vedo per aggirare il problema è impostare la codifica predefinita a livello di codice. Qualche altro suggerimento?

6
domanda forse irrilevante, ma c'è differenza quando UTF8 è impostato con "UTF8", "UTF-8" o "utf8". Recentemente ho scoperto che i contenitori EJB e WEB di IBM WAS 6.1 trattano in modo diverso le stringhe (in termini di distinzione tra maiuscole e minuscole) utilizzate per definire la codifica.
igor.beslic,

5
Solo un dettaglio: preferisci UTF-8 a UTF8 (solo il primo è standard). Questo vale ancora nel 2012 ...
Christophe Roussy,

4
L'impostazione o la lettura della file.encodingproprietà non è supportata .
McDowell,

@erickson Non sono ancora chiaro con la query, Non è vero che "file.encoding" è rilevante quando vengono utilizzati flussi I / O basati sui caratteri (tutte le sottoclassi di class Reader& class Writer)? Perché class FileInputStreamè un flusso I / O basato su byte, quindi perché uno dovrebbe preoccuparsi del set di caratteri nel flusso I / O basato su byte?
Scambio eccessivo il

Risposte:


312

Sfortunatamente, la file.encodingproprietà deve essere specificata all'avvio di JVM; al momento String.getBytes()dell'inserimento del metodo principale, la codifica dei caratteri utilizzata da e i costruttori predefiniti di InputStreamReadered OutputStreamWriterè stata memorizzata nella cache in modo permanente.

Come sottolinea Edward Grech, in un caso speciale come questo, la variabile d'ambiente JAVA_TOOL_OPTIONS può essere utilizzata per specificare questa proprietà, ma normalmente viene eseguita in questo modo:

java -Dfile.encoding=UTF-8  com.x.Main

Charset.defaultCharset()rifletterà le modifiche alla file.encodingproprietà, ma la maggior parte del codice nelle librerie Java principali che devono determinare la codifica dei caratteri predefinita non utilizza questo meccanismo.

Durante la codifica o la decodifica, è possibile eseguire una query sulla file.encodingproprietà o Charset.defaultCharset()trovare la codifica predefinita corrente e utilizzare il metodo appropriato o il sovraccarico del costruttore per specificarlo.


9
Per completezza, vorrei aggiungere che con un po 'di trucco puoi arrivare alla codifica predefinita effettivamente utilizzata (come viene memorizzata nella cache), grazie a Gary Cronin: byte [] byteArray = {' a '}; InputStream inputStream = new ByteArrayInputStream (byteArray); InputStreamReader reader = nuovo InputStreamReader (inputStream); String defaultEncoding = reader.getEncoding (); lists.xcf.berkeley.edu/lists/advanced-java/1999-October/…
Stijn de Witt

2
JDK-4163515 ha alcune informazioni in più sull'impostazione di file.encodingsysprop dopo l'avvio di JVM.
Caspar,

2
Mi stavo grattando la testa perché quel comando non funzionava perfettamente su Windows, Linux e Mac ... poi ho messo "attorno al valore in questo modo: java -D" file.encoding = UTF-8 "-jar
cabaji99

controlla la mia risposta in caso di Java Spring Boot: stackoverflow.com/a/48952844/986160
Michail Michailidis

170

Dalla documentazione dell'interfaccia dello strumento JVM ™ ...

Poiché non è sempre possibile accedere o modificare la riga di comando, ad esempio nelle VM incorporate o semplicemente nelle VM lanciate in profondità negli script, JAVA_TOOL_OPTIONSviene fornita una variabile in modo che gli agenti possano essere avviati in questi casi.

Impostando la variabile di ambiente (Windows) JAVA_TOOL_OPTIONSsu -Dfile.encoding=UTF8, la Systemproprietà (Java) verrà impostata automaticamente ogni volta che viene avviata una JVM. Saprai che il parametro è stato raccolto perché il seguente messaggio verrà pubblicato in System.err:

Picked up JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF8


Sai che l'istruzione "Raccolto ..." verrebbe stampata nei registri Tomcat?
thatidiotguy,

1
Ciao Edward Grech, ti ringrazio per la tua soluzione. È stato risolto il mio problema in un altro post del forum. stackoverflow.com/questions/14814230/…
Smaug,

8
UTF8o UTF-8?
Piccolo

1
@Tiny Java comprende entrambi. stackoverflow.com/questions/6031877/...
DLight

La tua soluzione mi ha fatto risparmiare tempo, grazie mille !!
Sobhan,

67

Ho un modo bizzarro che sicuramente funziona !!

System.setProperty("file.encoding","UTF-8");
Field charset = Charset.class.getDeclaredField("defaultCharset");
charset.setAccessible(true);
charset.set(null,null);

In questo modo ingannerai JVM che penserebbe che il set di caratteri non sia impostato e lo farà reimpostare su UTF-8, in fase di esecuzione!


2
NoSuchFieldException per me
SparK

10
Perché l'hacking funzioni, è necessario presumere che il gestore della sicurezza sia disattivato. Se non hai modo di impostare un flag JVM, potresti (probabilmente) avere anche un sistema abilitato per il gestore della sicurezza.
Yonatan,

3
JDK9 non approva più questo hack. WARNING: An illegal reflective access operation has occurred • WARNING: Illegal reflective access by [..] • WARNING: Please consider reporting this to the maintainers of [..] • WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations • WARNING: All illegal access operations will be denied in a future release
dotwin,

1
@Enerccio: Questa non è una buona risposta, è un trucco sporco e un problema in attesa di accadere. Questo dovrebbe essere usato solo come misura di emergenza.
sleske

1
@Enerccio: È discutibile se Java "dovrebbe" avere un modo per impostare questo - si potrebbe anche sostenere che gli sviluppatori "dovrebbero" specificare esplicitamente la codifica ogni volta che è rilevante. In ogni caso, questa soluzione ha il potenziale di causare seri problemi a lungo termine, da cui la segnalazione "solo per uso di emergenza". In realtà, l'uso di emergenza anche è discutibile, perché non v'è un modo supportato di farlo, impostando JAVA_TOOL_OPTIONS come spiegato in un'altra risposta.
sleske,

38

Penso che un approccio migliore rispetto all'impostazione del set di caratteri predefinito della piattaforma, specialmente se sembra che tu abbia restrizioni sull'impiego dell'applicazione, per non parlare della piattaforma, è chiamare il molto più sicuro String.getBytes("charsetName"). In questo modo l'applicazione non dipende da cose al di fuori del suo controllo.

Personalmente ritengo che String.getBytes()dovrebbe essere deprecato, in quanto ha causato seri problemi in una serie di casi che ho visto, in cui lo sviluppatore non ha tenuto conto del set di caratteri predefinito che potrebbe cambiare.


18

Non riesco a rispondere alla tua domanda originale, ma vorrei offrirti alcuni consigli, non dipendere dalla codifica predefinita di JVM. È sempre meglio specificare esplicitamente la codifica desiderata (ad esempio "UTF-8") nel codice. In questo modo, sai che funzionerà anche su diversi sistemi e configurazioni JVM.


7
Tranne, ovviamente, se stai scrivendo un'app desktop ed elaborando del testo specificato dall'utente che non ha metadati di codifica, allora la codifica predefinita della piattaforma è la tua ipotesi migliore su ciò che l'utente potrebbe utilizzare.
Michael Borgwardt,

@MichaelBorgwardt "allora la codifica predefinita della piattaforma è la tua ipotesi migliore" sembra che tu stia consigliando che voler cambiare l'impostazione predefinita non è una buona idea. Vuoi dire, utilizzare una codifica esplicita laddove possibile, usando il dafault fornito quando nient'altro è possibile?
Raedwald,

1
@Raedwald: sì, questo è ciò che intendevo. La codifica predefinita della piattaforma è (almeno su un computer dell'utente finale) quali sono generalmente gli utenti nelle impostazioni locali su cui è impostato il sistema. Queste sono informazioni che dovresti usare se non disponi di informazioni migliori (cioè specifiche del documento).
Michael Borgwardt,

1
@MichaelBorgwardt Nonsense. Utilizzare una libreria per rilevare automaticamente la codifica di input e salvare come Unicode con BOM. Questo è l'unico modo per affrontare e combattere la codifica dell'inferno.
Aleksandr Dubinsky,

Penso che voi due non siete nella stessa pagina. Michael parla di decodifica mentre Raedwald parla di elaborazione dopo la decodifica.
WesternGun

12

Prova questo :

    new OutputStreamWriter( new FileOutputStream("Your_file_fullpath" ),Charset.forName("UTF8"))

5

Avevamo gli stessi problemi. Abbiamo metodicamente provato diversi suggerimenti da questo articolo (e altri) senza alcun risultato. Abbiamo anche provato ad aggiungere il -Dfile.encoding=UTF8e nulla sembrava funzionare.

Per le persone che stanno avendo questo problema, il seguente articolo, infine, ci ha aiutato a rintracciare descrive come le impostazioni internazionali può rompere unicode/UTF-8inJava/Tomcat

http://www.jvmhost.com/articles/locale-breaks-unicode-utf-8-java-tomcat

Impostare correttamente le impostazioni internazionali nel ~/.bashrcfile ha funzionato per noi.


4

Ho provato molte cose, ma il codice di esempio qui funziona perfettamente. collegamento

Il punto cruciale del codice è:

String s = "एक गाव में एक किसान";
String out = new String(s.getBytes("UTF-8"), "ISO-8859-1");

4

Nel caso in cui si utilizzi Spring Boot e si desideri passare l'argomento file.encodingin JVM, è necessario eseguirlo in questo modo:

mvn spring-boot:run -Drun.jvmArguments="-Dfile.encoding=UTF-8"

questo era necessario per noi poiché stavamo usando i JTwigtemplate e il sistema operativo aveva ANSI_X3.4-1968scopertoSystem.out.println(System.getProperty("file.encoding"));

Spero che questo aiuti qualcuno!


2

Sto usando Amazon (AWS) Elastic Beanstalk e l'ho cambiato con successo in UTF-8.

In Elastic Beanstalk, vai a Configurazione> Software, "Proprietà dell'ambiente". Aggiungi (nome) JAVA_TOOL_OPTIONS con (valore) -Dfile.encoding = UTF8

Dopo il salvataggio, l'ambiente verrà riavviato con la codifica UTF-8.


1

Non hai chiarito cosa fai e non hai il controllo a questo punto. Se è possibile interporre una diversa classe OutputStream sul file di destinazione, è possibile utilizzare un sottotipo di OutputStream che converte String in byte in un set di caratteri definito, ad esempio UTF-8. Se UTF-8 modificato è sufficiente per le tue esigenze, puoi utilizzare DataOutputStream.writeUTF(String):

byte inbytes[] = new byte[1024];
FileInputStream fis = new FileInputStream("response.txt");
fis.read(inbytes);
String in = new String(inbytes, "UTF8");
DataOutputStream out = new DataOutputStream(new FileOutputStream("response-2.txt"));
out.writeUTF(in); // no getBytes() here

Se questo approccio non è fattibile, può essere utile chiarire qui esattamente cosa è possibile e non è possibile controllare in termini di flusso di dati e ambiente di esecuzione (anche se so che a volte è più facile dirlo che determinarlo). In bocca al lupo.


5
DataInputStream e DataOutputStream sono classi per scopi speciali che non devono mai essere utilizzate con file di testo semplice. Le UTF-8 modificate che utilizzano non sono compatibili con le UTF-8 reali. Inoltre, se l'OP potesse utilizzare la tua soluzione, potrebbe anche utilizzare lo strumento giusto per questo lavoro: un OutputStreamWriter.
Alan Moore,

1
mvn clean install -Dfile.encoding=UTF-8 -Dmaven.repo.local=/path-to-m2

Il comando ha funzionato con exec-maven-plugin per risolvere il seguente errore durante la configurazione di un'attività jenkins.

Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=512m; support was removed in 8.0
Error occurred during initialization of VM
java.nio.charset.IllegalCharsetNameException: "UTF-8"
    at java.nio.charset.Charset.checkName(Charset.java:315)
    at java.nio.charset.Charset.lookup2(Charset.java:484)
    at java.nio.charset.Charset.lookup(Charset.java:464)
    at java.nio.charset.Charset.defaultCharset(Charset.java:609)
    at sun.nio.cs.StreamEncoder.forOutputStreamWriter(StreamEncoder.java:56)
    at java.io.OutputStreamWriter.<init>(OutputStreamWriter.java:111)
    at java.io.PrintStream.<init>(PrintStream.java:104)
    at java.io.PrintStream.<init>(PrintStream.java:151)
    at java.lang.System.newPrintStream(System.java:1148)
    at java.lang.System.initializeSystemClass(System.java:1192)

0

Abbiamo impostato due proprietà di sistema insieme e questo fa sì che il sistema prenda tutto in utf8

file.encoding=UTF8
client.encoding.overrideUTF-8

7
La proprietà client.encoding.override sembra essere specifica di WebSphere.
Christophe Roussy,


0

Recentemente mi sono imbattuto nel sistema Notes 6.5 di un'azienda locale e ho scoperto che la webmail avrebbe mostrato caratteri non identificabili su un'installazione Windows localizzata non Zhongwen. Ho scavato per diverse settimane online, capito pochi minuti fa:

Nelle proprietà Java, aggiungere la seguente stringa ai parametri di runtime

-Dfile.encoding=MS950 -Duser.language=zh -Duser.country=TW -Dsun.jnu.encoding=MS950

In questo caso l'impostazione UTF-8 non funzionerebbe.


0

Il mio team ha riscontrato lo stesso problema nelle macchine con Windows .. quindi è riuscito a risolverlo in due modi:

a) Impostare la variabile di ambiente (anche nelle preferenze di sistema di Windows)

JAVA_TOOL_OPTIONS
-Dfile.encoding = UTF8

b) Introduci il seguente frammento nel tuo pom.xml:

 -Dfile.encoding=UTF-8 

ENTRO

 <jvmArguments>
 -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8001
 -Dfile.encoding=UTF-8
 </jvmArguments>
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.