Festlegen der Standard-Java-Zeichenkodierung

362

Wie stelle ich die von der JVM (1.5.x) programmgesteuert verwendete Standardzeichenkodierung richtig ein?

Ich habe gelesen, dass dies -Dfile.encoding=whateverfrüher der Weg für ältere JVMs war. Ich habe diesen Luxus aus Gründen, auf die ich nicht eingehen werde.

Ich habe versucht:

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

Die Eigenschaft wird festgelegt, aber es scheint nicht zu bewirken, dass der letzte getBytesAufruf unten UTF8 verwendet:

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());
Willi Mentzel
quelle
Ausgezeichnete Kommentare Jungs - und Dinge, die ich mir schon gedacht habe. Leider gibt es einen zugrunde liegenden String.getBytes () -Aufruf, über den ich keine Kontrolle habe. Die einzige Möglichkeit, die ich derzeit sehe, besteht darin, die Standardcodierung programmgesteuert festzulegen. Irgendwelche anderen Vorschläge?
6
Vielleicht irrelevante Frage, aber gibt es einen Unterschied, wenn UTF8 mit "UTF8", "UTF-8" oder "utf8" eingestellt ist. Kürzlich stellte ich fest, dass IBM WAS 6.1 EJB- und WEB-Container Zeichenfolgen, die zum Definieren der Codierung verwendet werden, unterschiedlich behandeln (in Bezug auf die Groß- und Kleinschreibung).
igor.beslic
5
Nur ein Detail, aber: Bevorzugen Sie UTF-8 gegenüber UTF8 (nur das erstere ist Standard). Dies gilt auch im Jahr 2012 ...
Christophe Roussy
4
Das Festlegen oder Lesen der file.encodingEigenschaft wird nicht unterstützt .
McDowell
@erickson Ich bin immer noch nicht klar mit der Abfrage. Stimmt es nicht, dass "file.encoding" relevant ist, wenn zeichenbasierte E / A-Streams verwendet werden (alle Unterklassen von class Reader& class Writer)? Da class FileInputStreames sich um einen bytebasierten E / A-Stream handelt, warum sollte man sich also für den Zeichensatz im bytebasierten E / A-Stream interessieren?
Überaustausch

Antworten:

311

Leider muss die file.encodingEigenschaft beim Start der JVM angegeben werden. Zum Zeitpunkt der Eingabe Ihrer Hauptmethode wurden die von String.getBytes()und die Standardkonstruktoren von InputStreamReaderund verwendete Zeichencodierung OutputStreamWriterdauerhaft zwischengespeichert.

Wie Edward Grech hervorhebt, kann in einem speziellen Fall wie diesem die Umgebungsvariable JAVA_TOOL_OPTIONS verwendet werden, um diese Eigenschaft anzugeben, aber normalerweise geschieht dies folgendermaßen:

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

Charset.defaultCharset()spiegelt Änderungen an der file.encodingEigenschaft wider , aber der größte Teil des Codes in den Java-Kernbibliotheken, der die Standardzeichencodierung bestimmen muss, verwendet diesen Mechanismus nicht.

Wenn Sie codieren oder decodieren, können Sie die file.encodingEigenschaft abfragen oder Charset.defaultCharset()die aktuelle Standardcodierung ermitteln und die entsprechende Methode oder Konstruktorüberladung verwenden, um sie anzugeben.

erickson
quelle
9
Der Vollständigkeit halber möchte ich hinzufügen, dass Sie dank Gary Cronin mit ein wenig Trick zur tatsächlich verwendeten Standardcodierung (wie im Cache) gelangen können: byte [] byteArray = {'a'}; InputStream inputStream = new ByteArrayInputStream (byteArray); InputStreamReader reader = neuer InputStreamReader (inputStream); String defaultEncoding = reader.getEncoding (); lists.xcf.berkeley.edu/lists/advanced-java/1999-October/…
Stijn de Witt
2
JDK-4163515 enthält weitere Informationen zum Festlegen des file.encodingSysprops nach dem Start der JVM.
Caspar
2
Ich habe mir am Kopf gekratzt, weil dieser Befehl unter Windows, Linux und Mac nicht perfekt funktioniert hat ... dann habe ich "um den Wert wie folgt gesetzt: java -D" file.encoding = UTF-8 "-jar
cabaji99
Überprüfen Sie meine Antwort im Fall von Java Spring Boot: stackoverflow.com/a/48952844/986160
Michail Michailidis
170

Aus der Dokumentation zur JVM ™ Tool-Schnittstelle

Da nicht immer auf die Befehlszeile zugegriffen oder diese geändert werden kann, z. B. in eingebetteten VMs oder einfach in VMs, die tief in Skripten gestartet werden, wird eine JAVA_TOOL_OPTIONSVariable bereitgestellt, damit in diesen Fällen Agenten gestartet werden können.

Wenn Sie die Umgebungsvariable (Windows) JAVA_TOOL_OPTIONSauf setzen -Dfile.encoding=UTF8, wird die SystemEigenschaft (Java) bei jedem Start einer JVM automatisch festgelegt. Sie werden wissen, dass der Parameter erfasst wurde, da die folgende Nachricht an gesendet wird System.err:

Picked up JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF8

Edward Grech
quelle
Wissen Sie, dass die Anweisung "Abgeholt ..." in Tomcat-Protokollen gedruckt wird?
Thatidiotguy
1
Hallo Edward Grech, ich danke dir für deine Lösung. Es wurde mein Problem in einem anderen Forumbeitrag gelöst. stackoverflow.com/questions/14814230/…
Smaug
8
UTF8oder UTF-8?
Winziger
1
@Tiny Java versteht beides. stackoverflow.com/questions/6031877/…
DLight
Ihre Lösung hat mir Zeit gespart, vielen Dank !!
Sobhan
67

Ich habe einen hackigen Weg, der definitiv funktioniert !!

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

Auf diese Weise werden Sie JVM austricksen, die denken würde, dass der Zeichensatz nicht gesetzt ist, und ihn zur Laufzeit erneut auf UTF-8 setzen!

Naskoos
quelle
2
NoSuchFieldException für mich
SparK
10
Damit der Hack funktioniert, müssen Sie davon ausgehen, dass der Sicherheitsmanager deaktiviert ist. Wenn Sie keine Möglichkeit haben, ein JVM-Flag zu setzen, verfügen Sie möglicherweise (wahrscheinlich) auch über ein Security Manager-fähiges System.
Yonatan
3
JDK9 ist nicht mehr von diesem Hack genehmigen. 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: Das ist keine gute Antwort, das ist ein schmutziger Hack und ein Problem, das darauf wartet, passiert zu werden. Das sollte nur als Notfallmaßnahme verwendet werden.
Sleske
1
@Enerccio: Es ist fraglich, ob Java eine Möglichkeit haben sollte, dies festzulegen - man könnte auch argumentieren, dass Entwickler die Codierung explizit angeben sollten, wann immer dies relevant ist. In jedem Fall kann diese Lösung auf längere Sicht ernsthafte Probleme verursachen, daher die Einschränkung "nur für den Notfall". Eigentlich ist sogar Notfall fraglich, denn es ist eine unterstützte Art und Weise tun, das Setzen von JAVA_TOOL_OPTIONS wie in einer anderen Antwort erklärt.
Sleske
38

Ich denke, ein besserer Ansatz als das Festlegen des Standardzeichensatzes der Plattform, insbesondere da Sie anscheinend Einschränkungen hinsichtlich der Auswirkungen auf die Anwendungsbereitstellung haben, geschweige denn auf die Plattform, besteht darin, den viel sichereren zu nennen String.getBytes("charsetName"). Auf diese Weise ist Ihre Anwendung nicht von Dingen abhängig, die außerhalb ihrer Kontrolle liegen.

Ich persönlich bin der Meinung, dass String.getBytes()dies veraltet sein sollte, da es in einer Reihe von Fällen, in denen der Entwickler den möglicherweise geänderten Standardzeichensatz nicht berücksichtigt hat, zu ernsthaften Problemen geführt hat.

Dov Wasserman
quelle
18

Ich kann Ihre ursprüngliche Frage nicht beantworten, möchte Ihnen jedoch einige Ratschläge geben - hängen Sie nicht von der Standardcodierung der JVM ab. Es ist immer am besten, die gewünschte Codierung (dh "UTF-8") explizit in Ihrem Code anzugeben. Auf diese Weise wissen Sie, dass es auch auf verschiedenen Systemen und JVM-Konfigurationen funktioniert.

Marc Novakowski
quelle
7
Außer natürlich, wenn Sie eine Desktop-App schreiben und einen benutzerdefinierten Text verarbeiten, der keine Codierungsmetadaten enthält, ist die Standardcodierung der Plattform Ihre beste Vermutung, was der Benutzer möglicherweise verwendet.
Michael Borgwardt
@MichaelBorgwardt "Dann ist die Standardkodierung der Plattform Ihre beste Vermutung" Sie scheinen zu raten, dass es keine so gute Idee ist , die Standardeinstellung ändern zu wollen . Meinen Sie, verwenden Sie nach Möglichkeit eine explizite Codierung mit dem angegebenen Standard, wenn nichts anderes möglich ist?
Raedwald
1
@ Raedwald: Ja, das habe ich gemeint. Die Plattform-Standardcodierung ist (zumindest auf einem Endbenutzercomputer), welche Benutzer in dem Gebietsschema, auf das das System eingestellt ist, normalerweise verwenden. Dies sind Informationen, die Sie verwenden sollten, wenn Sie keine besseren (dh dokumentenspezifischen) Informationen haben.
Michael Borgwardt
1
@ MichaelBorgwardt Unsinn. Verwenden Sie eine Bibliothek, um die Eingabecodierung automatisch zu erkennen und als Unicode mit Stückliste zu speichern. Nur so kann man mit der Kodierung der Hölle umgehen und sie bekämpfen.
Aleksandr Dubinsky
Ich denke ihr zwei seid nicht auf derselben Seite. Michael spricht über das Dekodieren, während Raedwald über das Verarbeiten nach dem Dekodieren spricht.
WesternGun
12

Versuche dies :

    new OutputStreamWriter( new FileOutputStream("Your_file_fullpath" ),Charset.forName("UTF8"))
Emmanuel.B
quelle
5

Wir hatten die gleichen Probleme. Wir haben einige Vorschläge aus diesem Artikel (und anderen) ohne Erfolg methodisch ausprobiert. Wir haben auch versucht, das hinzuzufügen -Dfile.encoding=UTF8und nichts schien zu funktionieren.

Für Menschen , die dieses Problem haben, schließlich die folgenden Artikel hat uns geholfen , beschreibt die Spur , wie die Ländereinstellung brechen kann unicode/UTF-8inJava/Tomcat

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

Das korrekte Einstellen des Gebietsschemas in der ~/.bashrcDatei hat bei uns funktioniert.

D Hell
quelle
4

Ich habe viele Dinge ausprobiert, aber der Beispielcode hier funktioniert perfekt. Verknüpfung

Der Kern des Codes ist:

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

Wenn Sie Spring Boot verwenden und das Argument file.encodingin JVM übergeben möchten, müssen Sie es folgendermaßen ausführen:

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

Dies wurde für uns benötigt, da wir JTwigVorlagen verwendeten und das Betriebssystem das hatte ANSI_X3.4-1968, was wir durch herausgefunden hattenSystem.out.println(System.getProperty("file.encoding"));

Hoffe das hilft jemandem!

Michail Michailidis
quelle
2

Ich verwende Amazon (AWS) Elastic Beanstalk und habe es erfolgreich in UTF-8 geändert.

Gehen Sie in Elastic Beanstalk zu Konfiguration> Software, "Umgebungseigenschaften". Fügen Sie (Name) JAVA_TOOL_OPTIONS mit (Wert) -Dfile.encoding = UTF8 hinzu

Nach dem Speichern wird die Umgebung mit der UTF-8-Codierung neu gestartet.

Berend Menninga
quelle
1

Sie wissen nicht genau, was Sie tun, und haben zu diesem Zeitpunkt noch keine Kontrolle darüber. Wenn Sie der Zieldatei eine andere OutputStream-Klasse hinzufügen können, können Sie einen Subtyp von OutputStream verwenden, der Strings unter einem von Ihnen definierten Zeichensatz in Bytes konvertiert, beispielsweise standardmäßig UTF-8. Wenn modifiziertes UTF-8 für Ihre Anforderungen ausreicht, können Sie Folgendes verwenden 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

Wenn dieser Ansatz nicht durchführbar ist, kann es hilfreich sein, wenn Sie hier genau klären, was Sie in Bezug auf Datenfluss und Ausführungsumgebung steuern können und was nicht (obwohl ich weiß, dass dies manchmal leichter gesagt als bestimmt ist). Viel Glück.

Dov Wasserman
quelle
5
DataInputStream und DataOutputStream sind Spezialklassen, die niemals mit Nur-Text-Dateien verwendet werden sollten. Das modifizierte UTF-8, das sie verwenden, ist nicht mit echtem UTF-8 kompatibel. Wenn das OP Ihre Lösung verwenden könnte, könnte es auch das richtige Tool für diesen Job verwenden: einen OutputStreamWriter.
Alan Moore
1
mvn clean install -Dfile.encoding=UTF-8 -Dmaven.repo.local=/path-to-m2

Der Befehl arbeitete mit dem Exec-Maven-Plugin zusammen, um den folgenden Fehler beim Konfigurieren einer Jenkins-Task zu beheben.

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)
Prabushi Samarakoon
quelle
0

Wir setzen dort zwei Systemeigenschaften zusammen und das System nimmt alles in utf8 auf

file.encoding=UTF8
client.encoding.overrideUTF-8
Lizi
quelle
7
Die Eigenschaft client.encoding.override scheint WebSphere-spezifisch zu sein.
Christophe Roussy
0

Kürzlich bin ich auf das Notes 6.5-System eines lokalen Unternehmens gestoßen und habe herausgefunden, dass Webmail bei einer nicht von Zhongwen lokalisierten Windows-Installation nicht identifizierbare Zeichen enthält. Habe mehrere Wochen online gegraben, habe es erst vor wenigen Minuten herausgefunden:

Fügen Sie in den Java-Eigenschaften die folgende Zeichenfolge zu den Laufzeitparametern hinzu

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

Die UTF-8-Einstellung würde in diesem Fall nicht funktionieren.

Midmaestro
quelle
0

Mein Team ist auf Computern mit Windows auf dasselbe Problem gestoßen. Anschließend konnte es auf zwei Arten behoben werden:

a) Legen Sie die Umgebungsvariable fest (auch in den Windows-Systemeinstellungen).

JAVA_TOOL_OPTIONS
-Dfile.encoding = UTF8

b) Führen Sie das folgende Snippet in Ihre pom.xml ein:

 -Dfile.encoding=UTF-8 

INNERHALB

 <jvmArguments>
 -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8001
 -Dfile.encoding=UTF-8
 </jvmArguments>
JacobTheKnitter
quelle