Définition du codage de caractères Java par défaut

362

Comment définir correctement l'encodage de caractères par défaut utilisé par la JVM (1.5.x) par programme?

J'ai lu que -Dfile.encoding=whateverc'était la voie à suivre pour les anciennes machines virtuelles Java. Je n'ai pas ce luxe pour des raisons que je n'entrerai pas.

J'ai essayé:

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

Et la propriété est définie, mais il ne semble pas que l' getBytesappel final ci-dessous utilise 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());
Willi Mentzel
la source
Excellents commentaires les gars - et des choses que je pensais déjà moi-même. Malheureusement, il existe un appel String.getBytes () sous-jacent sur lequel je n'ai aucun contrôle. La seule façon que je vois actuellement de le contourner est de définir le codage par défaut par programmation. D'autres suggestions?
6
question peut-être non pertinente, mais y a-t-il une différence lorsque UTF8 est défini avec "UTF8", "UTF-8" ou "utf8". Récemment, j'ai trouvé que les conteneurs EJB et WEB IBM WAS 6.1 traitent différemment (en termes de respect de la casse) les chaînes utilisées pour définir l'encodage.
igor.beslic
5
Juste un détail mais: préférez UTF-8 à UTF8 (seul le premier est standard). Cela s'applique toujours en 2012 ...
Christophe Roussy
4
La définition ou la lecture de la file.encodingpropriété n'est pas prise en charge .
McDowell
@erickson Je ne suis toujours pas clair avec la requête. N'est-il pas vrai que "file.encoding" est pertinent lorsque des flux d'E / S basés sur des caractères sont utilisés (toutes les sous-classes de class Reader& class Writer)? Parce class FileInputStreamque le flux d'E / S est basé sur les octets, alors pourquoi devrait-on se soucier du jeu de caractères dans le flux d'E / S basé sur les octets?
suréchange

Réponses:

312

Malheureusement, la file.encodingpropriété doit être spécifiée au démarrage de la JVM; au moment où votre méthode principale est entrée, le codage de caractères utilisé par String.getBytes()et les constructeurs par défaut de InputStreamReaderet OutputStreamWritera été mis en cache en permanence.

Comme le souligne Edward Grech, dans un cas spécial comme celui-ci, la variable d'environnement JAVA_TOOL_OPTIONS peut être utilisée pour spécifier cette propriété, mais cela se fait normalement comme ceci:

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

Charset.defaultCharset()reflétera les modifications apportées à la file.encodingpropriété, mais la plupart du code des bibliothèques Java de base qui doivent déterminer le codage de caractères par défaut n'utilise pas ce mécanisme.

Lorsque vous codez ou décodez, vous pouvez interroger la file.encodingpropriété ou Charset.defaultCharset()rechercher le codage par défaut actuel, et utiliser la méthode appropriée ou la surcharge du constructeur pour le spécifier.

erickson
la source
9
Pour être complet, je voudrais ajouter qu'avec un peu de ruse, vous pouvez obtenir l'encodage par défaut réellement utilisé (comme il est mis en cache), grâce à Gary Cronin: byte [] byteArray = {'a'}; InputStream inputStream = new ByteArrayInputStream (byteArray); Lecteur InputStreamReader = nouveau InputStreamReader (inputStream); String defaultEncoding = reader.getEncoding (); lists.xcf.berkeley.edu/lists/advanced-java/1999-October/…
Stijn de Witt
2
JDK-4163515 contient plus d'informations sur la configuration de file.encodingsysprop après le démarrage de la JVM.
Caspar
2
Je me grattais la tête parce que cette commande ne fonctionnait pas parfaitement sur Windows, linux et mac ... alors j'ai mis "autour de la valeur comme ceci: java -D" file.encoding = UTF-8 "-jar
cabaji99
vérifier ma réponse en cas de Java Spring Boot: stackoverflow.com/a/48952844/986160
Michail Michailidis
170

Dans la documentation de l' interface de l'outil JVM ™

Étant donné que la ligne de commande n'est pas toujours accessible ou modifiable, par exemple dans les VM intégrées ou simplement les VM lancées en profondeur dans les scripts, une JAVA_TOOL_OPTIONSvariable est fournie afin que les agents puissent être lancés dans ces cas.

En définissant la variable d'environnement (Windows) JAVA_TOOL_OPTIONSsur -Dfile.encoding=UTF8, la Systempropriété (Java) sera définie automatiquement à chaque démarrage d'une machine virtuelle Java . Vous saurez que le paramètre a été récupéré car le message suivant sera envoyé à System.err:

Picked up JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF8

Edward Grech
la source
Savez-vous que la déclaration "Ramassé ..." serait imprimée dans les journaux Tomcat?
thatidiotguy
1
Salut Edward Grech, je vous remercie pour votre solution. Il a été résolu mon problème dans un autre message du forum. stackoverflow.com/questions/14814230/…
Smaug
8
UTF8ou UTF-8?
Tiny
1
@Tiny Java comprend les deux. stackoverflow.com/questions/6031877/…
DLight
Votre solution m'a fait gagner du temps, merci beaucoup !!
Sobhan
67

J'ai une méthode hacky qui fonctionne vraiment !!

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

De cette façon, vous allez tromper JVM qui penserait que le jeu de caractères n'est pas défini et faire en sorte qu'il soit à nouveau défini sur UTF-8, lors de l'exécution!

naskoos
la source
2
NoSuchFieldException pour moi
SparK
10
Pour que le hack fonctionne, vous devez supposer que le gestionnaire de sécurité est désactivé. Si vous ne disposez pas d'un moyen de définir un indicateur JVM, vous pouvez également (probablement) avoir un système activé par le gestionnaire de sécurité.
Yonatan
3
JDK9 ne pas approuver de ce hack plus. 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: Ce n'est pas une bonne réponse, c'est un hack sale et un problème qui attend de se produire. Cela ne devrait être utilisé que comme mesure d'urgence.
sleske
1
@Enerccio: On peut se demander si Java "devrait" avoir un moyen de régler cela - on pourrait également affirmer que les développeurs "devraient" spécifier explicitement l'encodage chaque fois qu'il est pertinent. Quoi qu'il en soit, cette solution a le potentiel de causer de graves problèmes à long terme, d'où la mise en garde «à usage d'urgence uniquement». En fait, même une utilisation d'urgence est discutable, car il existe une méthode prise en charge, définissant JAVA_TOOL_OPTIONS comme expliqué dans une autre réponse.
sleske
38

Je pense qu'une meilleure approche que de définir le jeu de caractères par défaut de la plate-forme, d'autant plus que vous semblez avoir des restrictions sur le déploiement d'applications, sans parler de la plate-forme, est d'appeler le plus sûr String.getBytes("charsetName"). De cette façon, votre application ne dépend pas de choses indépendantes de sa volonté.

String.getBytes()Personnellement, je pense que cela devrait être déconseillé, car cela a causé de graves problèmes dans un certain nombre de cas que j'ai vus, où le développeur ne tenait pas compte du changement de jeu de caractères par défaut.

Dov Wasserman
la source
18

Je ne peux pas répondre à votre question initiale mais je voudrais vous offrir quelques conseils - ne dépendez pas de l'encodage par défaut de la JVM. Il est toujours préférable de spécifier explicitement l'encodage souhaité (c'est-à-dire "UTF-8") dans votre code. De cette façon, vous savez que cela fonctionnera même sur différents systèmes et configurations JVM.

Marc Novakowski
la source
7
Sauf, bien sûr, si vous écrivez une application de bureau et traitez du texte spécifié par l'utilisateur qui n'a pas de métadonnées d'encodage - alors l'encodage par défaut de la plateforme est votre meilleure estimation de ce que l'utilisateur pourrait utiliser.
Michael Borgwardt
@MichaelBorgwardt "alors l'encodage par défaut de la plate-forme est votre meilleure estimation", vous semblez conseiller que vouloir changer la valeur par défaut n'est pas une si bonne idée. Voulez-vous dire, utilisez un encodage explicite dans la mesure du possible, en utilisant le défaut fourni lorsque rien d'autre n'est possible?
Raedwald
1
@ Raedwald: oui, c'est ce que je voulais dire. Le codage par défaut de la plate-forme est (au moins sur une machine d'utilisateur final) ce que les utilisateurs des paramètres régionaux que le système est configuré utilisent généralement. Ce sont des informations que vous devez utiliser si vous n'avez pas de meilleures informations (c'est-à-dire spécifiques au document).
Michael Borgwardt, le
1
@MichaelBorgwardt Nonsense. Utilisez une bibliothèque pour détecter automatiquement le codage d'entrée et enregistrez-le sous Unicode avec BOM. C'est le seul moyen de gérer et de combattre l'enfer de l'encodage.
Aleksandr Dubinsky
Je pense que vous n'êtes pas tous les deux sur la même page. Michael parle de décodage tandis que Raedwald vous parle de traitement après décodage.
WesternGun
12

Essaye ça :

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

Nous avions les mêmes problèmes. Nous avons méthodiquement essayé plusieurs suggestions de cet article (et d'autres) en vain. Nous avons également essayé d'ajouter le -Dfile.encoding=UTF8et rien ne semblait fonctionner.

Pour les personnes qui éprouvent ce problème, l'article suivant finalement nous a aidés à traquer décrit comment la configuration locale peut briser unicode/UTF-8enJava/Tomcat

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

La définition correcte des paramètres régionaux dans le ~/.bashrcfichier a fonctionné pour nous.

D Bright
la source
4

J'ai essayé beaucoup de choses, mais l'exemple de code ici fonctionne parfaitement. Lien

Le nœud du code est:

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

Dans le cas où vous utilisez Spring Boot et souhaitez passer l'argument file.encodingdans JVM, vous devez l'exécuter comme ça:

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

cela était nécessaire pour nous car nous utilisions des JTwigmodèles et le système d'exploitation ANSI_X3.4-1968que nous avons découvert par le biaisSystem.out.println(System.getProperty("file.encoding"));

J'espère que cela aide quelqu'un!

Michail Michailidis
la source
2

J'utilise Amazon (AWS) Elastic Beanstalk et l'ai changé avec succès en UTF-8.

Dans Elastic Beanstalk, accédez à Configuration> Logiciel, "Propriétés d'environnement". Ajoutez (nom) JAVA_TOOL_OPTIONS avec (valeur) -Dfile.encoding = UTF8

Après l'enregistrement, l'environnement redémarrera avec l'encodage UTF-8.

Berend Menninga
la source
1

Pas clair sur ce que vous faites et n'avez pas de contrôle sur ce point. Si vous pouvez interposer une classe OutputStream différente sur le fichier de destination, vous pouvez utiliser un sous-type de OutputStream qui convertit les chaînes en octets sous un jeu de caractères que vous définissez, par exemple UTF-8 par défaut. Si l'UTF-8 modifié est suffisant pour vos besoins, vous pouvez utiliser 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

Si cette approche n'est pas réalisable, il peut être utile de clarifier ici exactement ce que vous pouvez et ne pouvez pas contrôler en termes de flux de données et d'environnement d'exécution (même si je sais que c'est parfois plus facile à dire qu'à déterminer). Bonne chance.

Dov Wasserman
la source
5
DataInputStream et DataOutputStream sont des classes spéciales qui ne doivent jamais être utilisées avec des fichiers de texte brut. L'UTF-8 modifié qu'ils utilisent n'est pas compatible avec l'UTF-8 réel. En outre, si l'OP pouvait utiliser votre solution, il pourrait également utiliser le bon outil pour ce travail: un OutputStreamWriter.
Alan Moore
1
mvn clean install -Dfile.encoding=UTF-8 -Dmaven.repo.local=/path-to-m2

La commande a fonctionné avec exec-maven-plugin pour résoudre l'erreur suivante lors de la configuration d'une tâche 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)
prabushi samarakoon
la source
0

Nous y avons défini deux propriétés système ensemble et le système prend tout en utf8

file.encoding=UTF8
client.encoding.overrideUTF-8
lizi
la source
7
La propriété client.encoding.override semble être spécifique à WebSphere.
Christophe Roussy
0

Récemment, je suis tombé sur le système Notes 6.5 d'une entreprise locale et j'ai découvert que le webmail afficherait des caractères non identifiables sur une installation Windows non Zhongwen. J'ai creusé pendant plusieurs semaines en ligne, je l'ai compris il y a quelques minutes:

Dans les propriétés Java, ajoutez la chaîne suivante aux paramètres d'exécution

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

Le réglage UTF-8 ne fonctionnerait pas dans ce cas.

midmaestro
la source
0

Mon équipe a rencontré le même problème sur les machines avec Windows .. puis a réussi à le résoudre de deux manières:

a) Définir la variable d'environnement (même dans les préférences système de Windows)

JAVA_TOOL_OPTIONS
-Dfile.encoding = UTF8

b) Introduisez l'extrait suivant dans votre pom.xml:

 -Dfile.encoding=UTF-8 

DANS

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