Configurando a Codificação de Caracteres Java Padrão

362

Como configuro corretamente a codificação de caracteres padrão usada pela JVM (1.5.x) programaticamente?

Eu li que -Dfile.encoding=whatevercostumava ser o caminho a seguir para JVMs mais antigas. Não tenho esse luxo por razões que não me envolvem.

Eu tentei:

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

E a propriedade é configurada, mas parece não fazer com que a getByteschamada final abaixo use 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
fonte
Excelentes comentários pessoal - e coisas que eu já estava pensando. Infelizmente, há uma chamada String.getBytes () subjacente sobre a qual eu não tenho controle. A única maneira que vejo atualmente para contornar isso é definir a codificação padrão programaticamente. Alguma outra sugestão?
6
pergunta talvez irrelevante, mas existe alguma diferença quando UTF8 é definido com "UTF8", "UTF-8" ou "utf8". Recentemente, descobri que os contêineres IBM WAS 6.1 EJB e WEB tratam de maneira diferente as strings (em distinção entre maiúsculas e minúsculas) usadas para definir a codificação.
Igor.beslic 29/05
5
Apenas um detalhe, mas: prefira UTF-8 a UTF8 (apenas o primeiro é padrão). Isso ainda se aplica em 2012 ...
Christophe Roussy 22/03
4
A configuração ou leitura da file.encodingpropriedade não é suportada .
24412 McDowell
@erickson Ainda não estou claro com a consulta. Não é verdade que "file.encoding" é relevante quando fluxos de E / S baseados em caracteres são usados ​​(todas as subclasses de class Reader& class Writer)? Como class FileInputStreamo fluxo de E / S é baseado em bytes, por que alguém deveria se preocupar com o conjunto de caracteres no fluxo de E / S baseado em bytes?
usar o seguinte código

Respostas:

312

Infelizmente, a file.encodingpropriedade precisa ser especificada quando a JVM é inicializada; no momento em que o método principal é inserido, a codificação de caracteres usada pelos String.getBytes()construtores padrão InputStreamReadere OutputStreamWriterfoi permanentemente armazenada em cache.

Como Edward Grech aponta, em um caso especial como este, a variável de ambiente JAVA_TOOL_OPTIONS pode ser usada para especificar essa propriedade, mas normalmente é assim:

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

Charset.defaultCharset()refletirá as alterações na file.encodingpropriedade, mas a maioria do código nas bibliotecas Java principais que precisam determinar a codificação de caracteres padrão não usa esse mecanismo.

Ao codificar ou decodificar, você pode consultar a file.encodingpropriedade ou Charset.defaultCharset()encontrar a codificação padrão atual e usar o método apropriado ou a sobrecarga do construtor para especificá-la.

erickson
fonte
9
Para completar, gostaria de acrescentar que, com um pouco de truque, você pode obter a codificação padrão realmente usada (como é armazenada em cache), graças a Gary Cronin: byte [] byteArray = {'a'}; InputStream inputStream = novo ByteArrayInputStream (byteArray); Leitor InputStreamReader = novo InputStreamReader (inputStream); String defaultEncoding = reader.getEncoding (); lists.xcf.berkeley.edu/lists/advanced-java/1999-October/…
Stijn de Witt
2
O JDK-4163515 possui mais algumas informações sobre a configuração do file.encodingsysprop após a inicialização da JVM.
Caspar
2
Eu estava coçando a cabeça porque esse comando não estava funcionando perfeitamente no Windows, linux e mac ... então eu coloquei "em torno do valor dessa forma: java -D" file.encoding = UTF-8 "-jar
cabaji99
verifique minha resposta no caso do Java Spring Boot: stackoverflow.com/a/48952844/986160
Michail Michailidis
170

Na documentação da interface da ferramenta JVM ™

Como a linha de comando nem sempre pode ser acessada ou modificada, por exemplo, em VMs incorporadas ou simplesmente VMs ativadas profundamente nos scripts, uma JAVA_TOOL_OPTIONSvariável é fornecida para que os agentes possam ser iniciados nesses casos.

Ao definir a variável de ambiente (Windows) JAVA_TOOL_OPTIONScomo -Dfile.encoding=UTF8, a Systempropriedade (Java) será configurada automaticamente sempre que uma JVM for iniciada. Você saberá que o parâmetro foi escolhido porque a seguinte mensagem será publicada em System.err:

Picked up JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF8

Edward Grech
fonte
Você sabia que a instrução "Picked up ..." seria impressa nos logs do Tomcat?
thatidiotguy
11
Olá Edward Grech Agradeço a sua solução. Foi resolvido meu problema em outro post do fórum. stackoverflow.com/questions/14814230/…
Smaug
8
UTF8ou UTF-8?
minúsculo
11
O @Tiny Java entende os dois. stackoverflow.com/questions/6031877/...
DLight
Sua solução economizou meu tempo, muito obrigado !!
Sobhan 15/03
67

Eu tenho uma maneira hacky que definitivamente funciona !!

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

Dessa forma, você enganará a JVM, que pensaria que o charset não está definido e o configuraria novamente para UTF-8, em tempo de execução!

naskoos
fonte
2
NoSuchFieldException para mim
SparK 13/03
10
Para que o hack funcione, você precisa assumir que o gerenciador de segurança está desativado. Se você não tiver uma maneira de definir um sinalizador da JVM, poderá (provavelmente) também ter um sistema ativado pelo gerenciador de segurança.
Yonatan
3
O JDK9 não aprova mais esse 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
11
@Enerccio: Essa não é uma boa resposta, é um truque sujo e um problema esperando para acontecer. Isso deve ser usado apenas como uma medida de emergência.
sleske
11
@Enerccio: É discutível se o Java "deveria" ter uma maneira de definir isso - também se pode argumentar que os desenvolvedores "deveriam" especificar explicitamente a codificação sempre que for relevante. De qualquer forma, esta solução tem o potencial de causar sérios problemas a longo prazo, daí a ressalva "apenas para uso emergencial". Na verdade, até mesmo o uso de emergência é questionável, porque não é uma forma suportada de fazê-lo, definindo JAVA_TOOL_OPTIONS como explicado em outra resposta.
sleske
38

Eu acho que uma abordagem melhor do que definir o conjunto de caracteres padrão da plataforma, especialmente porque você parece ter restrições em afetar a implantação do aplicativo, e muito menos a plataforma, é chamar de muito mais seguro String.getBytes("charsetName"). Dessa forma, seu aplicativo não depende de coisas além de seu controle.

Pessoalmente, acho que isso String.getBytes()deve ser descontinuado, pois causou sérios problemas em vários casos que já vi, nos quais o desenvolvedor não considerou o charset padrão possivelmente alterado.

Dov Wasserman
fonte
18

Não consigo responder à sua pergunta original, mas gostaria de lhe oferecer alguns conselhos - não dependa da codificação padrão da JVM. É sempre melhor especificar explicitamente a codificação desejada (por exemplo, "UTF-8") no seu código. Dessa forma, você sabe que funcionará mesmo em diferentes sistemas e configurações de JVM.

Marc Novakowski
fonte
7
Exceto, é claro, se você estiver escrevendo um aplicativo de desktop e processando algum texto especificado pelo usuário que não possui metadados de codificação - a codificação padrão da plataforma é sua melhor estimativa do que o usuário pode estar usando.
Michael Borgwardt
@MichaelBorgwardt ", então a codificação padrão da plataforma é o seu melhor palpite", você parece estar avisando que querer alterar o padrão não é uma boa idéia. Você quer dizer, use uma codificação explícita sempre que possível, usando o padrão fornecido quando nada mais for possível?
Raedwald
11
@ Raedwald: sim, foi o que eu quis dizer. A codificação padrão da plataforma é (pelo menos em uma máquina do usuário final) em que os usuários no código de idioma no qual o sistema está configurado normalmente estão usando. Essas são as informações que você deve usar se não tiver informações melhores (ou seja, específicas de documentos).
Michael Borgwardt
11
@MichaelBorgwardt Nonsense. Use uma biblioteca para detectar automaticamente a codificação de entrada e salve como Unicode com a BOM. Essa é a única maneira de lidar e combater o inferno da codificação.
Aleksandr Dubinsky
Eu acho que vocês dois não estão na mesma página. Michael fala sobre decodificação enquanto Raedwald você fala sobre processamento após decodificação.
WesternGun 21/01
12

Tente o seguinte:

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

Estávamos tendo os mesmos problemas. Metodologicamente, tentamos várias sugestões deste artigo (e de outros) sem sucesso. Também tentamos adicionar o -Dfile.encoding=UTF8e nada parecia estar funcionando.

Para as pessoas que estão tendo este problema, o seguinte artigo finalmente nos ajudou a rastrear descreve como a definição de região pode quebrar unicode/UTF-8emJava/Tomcat

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

Definir o código do idioma corretamente no ~/.bashrcarquivo funcionou para nós.

D Brilhante
fonte
4

Eu tentei muitas coisas, mas o código de exemplo aqui funciona perfeitamente. Ligação

O cerne do código é:

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

Caso você esteja usando o Spring Boot e deseje passar o argumento file.encodingna JVM, execute-o assim:

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

isso foi necessário para nós, pois estávamos usando JTwigmodelos e o sistema operacional tinha ANSI_X3.4-1968descoberto por meio deSystem.out.println(System.getProperty("file.encoding"));

Espero que isso ajude alguém!

Michail Michailidis
fonte
2

Estou usando o Amazon (AWS) Elastic Beanstalk e o alterei com sucesso para UTF-8.

No Elastic Beanstalk, vá para Configuração> Software, "Propriedades do ambiente". Inclua (nome) JAVA_TOOL_OPTIONS com (value) -Dfile.encoding = UTF8

Após salvar, o ambiente será reiniciado com a codificação UTF-8.

Berend Menninga
fonte
1

Não está claro o que você faz e não tem controle neste momento. Se você puder interpor uma classe OutputStream diferente no arquivo de destino, poderá usar um subtipo de OutputStream que converta Strings em bytes em um conjunto de caracteres definido, por exemplo, UTF-8. Se o UTF-8 modificado for suficiente para suas necessidades, você poderá usar 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 essa abordagem não for viável, pode ser útil esclarecer aqui exatamente o que você pode e não pode controlar em termos de fluxo de dados e ambiente de execução (embora eu saiba que às vezes é mais fácil dizer do que determinar). Boa sorte.

Dov Wasserman
fonte
5
DataInputStream e DataOutputStream são classes de finalidade especial que nunca devem ser usadas com arquivos de texto sem formatação. O UTF-8 modificado que eles empregam não é compatível com o UTF-8 real. Além disso, se o OP pudesse usar sua solução, ele também poderia usar a ferramenta certa para este trabalho: um OutputStreamWriter.
Alan Moore
1
mvn clean install -Dfile.encoding=UTF-8 -Dmaven.repo.local=/path-to-m2

O comando trabalhou com exec-maven-plugin para resolver o seguinte erro ao configurar uma tarefa 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
fonte
0

Definimos duas propriedades do sistema juntas e isso faz com que o sistema leve tudo para o utf8

file.encoding=UTF8
client.encoding.overrideUTF-8
lizi
fonte
7
A propriedade client.encoding.override parece ser específica do WebSphere.
Christophe Roussy
0

Recentemente, deparei-me com o sistema Notes 6.5 de uma empresa local e descobri que o webmail mostraria caracteres não identificáveis ​​em uma instalação do Windows que não fosse Zhongwen. Pesquisei por várias semanas on-line, descobri isso há apenas alguns minutos:

Nas propriedades Java, inclua a seguinte sequência em Parâmetros de Tempo de Execução

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

A configuração UTF-8 não funcionaria neste caso.

midmaestro
fonte
0

Minha equipe encontrou o mesmo problema em máquinas com Windows. Em seguida, conseguiu resolvê-lo de duas maneiras:

a) Defina a variável de ambiente (mesmo nas preferências do sistema Windows)

JAVA_TOOL_OPTIONS
-Dfile.encoding = UTF8

b) Introduza o seguinte snippet no seu pom.xml:

 -Dfile.encoding=UTF-8 

DENTRO

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