A resposta óbvia é usar, Charset.defaultCharset()
mas descobrimos recentemente que essa pode não ser a resposta certa. Disseram-me que o resultado é diferente do conjunto de caracteres padrão real usado pelas classes java.io em várias ocasiões. Parece que o Java mantém 2 conjuntos de charset padrão. Alguém tem alguma ideia sobre este assunto?
Conseguimos reproduzir um caso de falha. É uma espécie de erro do usuário, mas ainda pode expor a causa raiz de todos os outros problemas. Aqui está o código,
public class CharSetTest {
public static void main(String[] args) {
System.out.println("Default Charset=" + Charset.defaultCharset());
System.setProperty("file.encoding", "Latin-1");
System.out.println("file.encoding=" + System.getProperty("file.encoding"));
System.out.println("Default Charset=" + Charset.defaultCharset());
System.out.println("Default Charset in Use=" + getDefaultCharSet());
}
private static String getDefaultCharSet() {
OutputStreamWriter writer = new OutputStreamWriter(new ByteArrayOutputStream());
String enc = writer.getEncoding();
return enc;
}
}
Nosso servidor requer conjunto de caracteres padrão em Latin-1 para lidar com alguma codificação mista (ANSI / Latin-1 / UTF-8) em um protocolo legado. Portanto, todos os nossos servidores funcionam com este parâmetro JVM,
-Dfile.encoding=ISO-8859-1
Aqui está o resultado em Java 5,
Default Charset=ISO-8859-1
file.encoding=Latin-1
Default Charset=UTF-8
Default Charset in Use=ISO8859_1
Alguém tenta alterar o tempo de execução da codificação definindo file.encoding no código. Todos nós sabemos que isso não funciona. No entanto, isso aparentemente joga fora defaultCharset (), mas não afeta o charset padrão real usado por OutputStreamWriter.
Isso é um bug ou recurso?
EDIT: A resposta aceita mostra a causa raiz do problema. Basicamente, você não pode confiar em defaultCharset () em Java 5, que não é a codificação padrão usada pelas classes de E / S. Parece que o Java 6 corrige esse problema.
fonte
Respostas:
Isso é realmente estranho ... Uma vez definido, o Charset padrão é armazenado em cache e não é alterado enquanto a classe está na memória. Definir a
"file.encoding"
propriedade comSystem.setProperty("file.encoding", "Latin-1");
não faz nada. Cada vez queCharset.defaultCharset()
é chamado, ele retorna o conjunto de caracteres em cache.Aqui estão meus resultados:
Estou usando JVM 1.6.
(atualizar)
Está bem. Eu reproduzi seu bug com JVM 1.5.
Olhando para o código-fonte de 1.5, o conjunto de caracteres padrão em cache não está sendo definido. Não sei se isso é um bug ou não, mas 1.6 muda essa implementação e usa o conjunto de caracteres em cache:
JVM 1.5:
JVM 1.6:
Quando você definir a codificação do arquivo para
file.encoding=Latin-1
a próxima vez que você chamarCharset.defaultCharset()
, o que acontece é que, como o conjunto de caracteres padrão em cache não foi definido, ele tentará encontrar o conjunto de caracteres apropriado para o nomeLatin-1
. Este nome não foi encontrado porque está incorreto e retorna o padrãoUTF-8
.Quanto ao motivo de as classes IO
OutputStreamWriter
retornarem um resultado inesperado,a implementação de
sun.nio.cs.StreamEncoder
(que é usada por essas classes IO) é diferente também para JVM 1.5 e JVM 1.6. A implementação da JVM 1.6 é baseada noCharset.defaultCharset()
método para obter a codificação padrão, se uma não for fornecida para as classes de E / S. A implementação do JVM 1.5 usa um método diferenteConverters.getDefaultEncodingName();
para obter o conjunto de caracteres padrão. Este método usa seu próprio cache do conjunto de caracteres padrão que é definido na inicialização da JVM:JVM 1.6:
JVM 1.5:
Mas concordo com os comentários. Você não deve confiar nesta propriedade . É um detalhe de implementação.
fonte
Parece um comportamento indefinido. Eu sei que, na prática, você pode alterar a codificação padrão usando uma propriedade de linha de comando, mas não acho que o que acontece quando você faz isso está definido.
ID do bug: 4153515 em problemas ao definir esta propriedade:
Eu me encolho quando vejo pessoas definindo a codificação na linha de comando - você não sabe qual código isso afetará.
Se você não quiser usar a codificação padrão, defina a codificação que deseja explicitamente por meio do método / construtor apropriado .
fonte
Primeiro, Latin-1 é o mesmo que ISO-8859-1, então, o padrão já estava OK para você. Certo?
Você definiu com sucesso a codificação para ISO-8859-1 com seu parâmetro de linha de comando. Você também o define programaticamente para "Latin-1", mas esse não é um valor reconhecido de uma codificação de arquivo para Java. Consulte http://java.sun.com/javase/6/docs/technotes/guides/intl/encoding.doc.html
Quando você faz isso, parece que o Charset é redefinido para UTF-8, olhando para a fonte. Isso pelo menos explica a maior parte do comportamento.
Não sei por que OutputStreamWriter mostra ISO8859_1. Ele delega para classes sun.misc. * De código-fonte fechado. Suponho que não esteja lidando exatamente com a codificação pelo mesmo mecanismo, o que é estranho.
Mas é claro que você deve sempre especificar qual codificação você quer dizer neste código. Eu nunca confiaria no padrão da plataforma.
fonte
O comportamento não é tão estranho. Olhando para a implementação das classes, é causado por:
Charset.defaultCharset()
não está armazenando em cache o conjunto de caracteres determinado em Java 5.Charset.defaultCharset()
novamente causa uma segunda avaliação da propriedade do sistema, nenhum caractere definido com o nome "Latin-1" é encontrado, então oCharset.defaultCharset()
padrão é "UTF-8".OutputStreamWriter
entanto, o está armazenando em cache o conjunto de caracteres padrão e provavelmente já é usado durante a inicialização da VM, de modo que seu conjunto de caracteres padrão divergeCharset.defaultCharset()
se a propriedade do sistema "file.encoding" tiver sido alterada no tempo de execução.Como já apontado, não está documentado como a VM deve se comportar em tal situação. A
Charset.defaultCharset()
documentação da API não é muito precisa sobre como o conjunto de caracteres padrão é determinado, apenas mencionando que isso geralmente é feito na inicialização da VM, com base em fatores como o conjunto de caracteres padrão do sistema operacional ou localidade padrão.fonte
Eu defini o argumento vm no servidor WAS como -Dfile.encoding = UTF-8 para alterar o conjunto de caracteres padrão dos servidores.
fonte
Verifica
parece ser a mesma codificação usada na linha de comando do seu sistema.
fonte