Por que nomes de conjuntos de caracteres não são constantes?

211

Os problemas de charset são confusos e complicados por si mesmos, mas, além disso, é necessário lembrar os nomes exatos dos seus charsets. É isso "utf8"? Ou "utf-8"? Ou talvez "UTF-8"? Ao pesquisar exemplos de código na Internet, você verá todas as opções acima. Por que não fazê-los nomeados constantes e usar Charset.UTF8?

serg
fonte
19
+1: Isso também estava me incomodando o tempo todo. A mesma história continua MessageDigest#getInstance()a propósito.
BalusC
2
Para a resposta real, você precisa perguntar a alguém na Sun. Boa sorte com isso :-)
Stephen C
1
Stephen C: Acredito que tenha sido discutido em uma lista de discussão pública. -Alguém no sol.
Tom Hawtin # tackline
1
veja esta pergunta
yegor256

Respostas:

160

A resposta simples para a pergunta é que as seqüências de caracteres disponíveis variam de plataforma para plataforma.

No entanto, há seis que precisam estar presentes, portanto, constantes poderiam ter sido feitas para aquelas há muito tempo. Não sei por que não estavam.

O JDK 1.4 fez uma grande coisa ao introduzir o tipo Charset. Nesse momento, eles não desejariam mais fornecer constantes String, pois o objetivo é fazer com que todos usem instâncias de Charset. Então, por que não fornecer as seis constantes padrão do Charset? Perguntei a Martin Buchholz, já que ele estava sentado ao meu lado e ele disse que não havia um motivo realmente particularmente bom, exceto que, na época, as coisas ainda estavam pela metade - poucas APIs do JDK haviam sido adaptadas para aceitar Charset, e dos que eram, as sobrecargas de Charset geralmente tiveram um desempenho um pouco pior.

É triste que seja apenas no JDK 1.6 que eles finalmente terminaram de equipar tudo com sobrecargas no Charset. E que essa situação de desempenho reverso ainda existe (a razão pela qual é incrivelmente estranha e eu não posso explicar, mas está relacionada à segurança!).

Para encurtar a história - apenas defina suas próprias constantes ou use a classe Charsets da Guava à qual Tony the Pony se vinculou (embora essa biblioteca ainda não tenha sido realmente lançada).

Atualização: uma StandardCharsetsclasse está no JDK 7.

Kevin Bourrillion
fonte
Apenas curioso, alguma idéia de quando haverá um lançamento (alfa / beta / o que for) do Goiaba? A página inicial do projeto é um pouco curta sobre isso.
Jonik
Não há peru para mim até sair!
Kevin Bourrillion
a razão pela qual é incrivelmente estranha e eu não posso explicar, mas está relacionada à segurança - você pode criar uma String modificável através de conjuntos de caracteres personalizados, mas eles poderiam ter sido feitos trabalhos ainda mais rápido que a string (que na verdade procura o conjunto de caracteres). É uma omissão / negligência como String(byte bytes[], int offset, int length, Charset charset)é implementado. De fato, o impacto no desempenho não é trivial ao criar uma cadeia pequena a partir de um byte grande [].
bestsss 16/01
7
Não é justo! Você tem acesso a esses ótimos recursos. = (Eu vi uma outra resposta, onde uma vez disse, "Sim, então eu perguntei Josh [Bloch] sobre isso ..."
kevinarpe
O PrintStream não suporta Charset
rofrol
102

Dois anos depois, o StandardCharsets do Java 7 agora define constantes para os 6 charsets padrão.

Se você está preso no Java 5/6, pode usar constantes Charsets do Guava , conforme sugerido por Kevin Bourrillion e Jon Skeet.

Etienne Neveu
fonte
29

Eu diria que podemos fazer muito melhor do que isso ... por que os conjuntos de caracteres com garantia de disponibilidade não são acessíveis diretamente? Charset.UTF8deve ser uma referência ao Charset, não ao nome como uma string. Dessa forma, não teríamos que lidar com UnsupportedEncodingExceptiontodos os lados.

Lembre-se, também acho que o .NET escolheu uma estratégia melhor por padrão em UTF-8 em todos os lugares. Em seguida, ele estragou o nome da propriedade de codificação "sistema operacional padrão" simplesmente Encoding.Default- o que não é o padrão no próprio .NET :(

Voltando a reclamar sobre o suporte a charset do Java - por que não existe um construtor para FileWriter/ FileReaderque leva um Charset? Basicamente, essas são classes quase inúteis devido a essa restrição - você quase sempre precisa de InputStreamReadercerca de a FileInputStreamou equivalente para a saída :(

Enfermeira, enfermeira - onde está meu remédio?

Edição: Ocorre-me que isso realmente não respondeu à pergunta. A resposta real é presumivelmente "ninguém envolvido pensou nisso" ou "alguém envolvido pensou que era uma má idéia". Eu sugeriria fortemente que as classes de utilidade internas que fornecem os nomes ou conjuntos de caracteres evitem duplicação em torno da base de código ... Ou você pode simplesmente usar o que usamos no Google quando esta resposta foi escrita pela primeira vez . (Observe que, a partir do Java 7, você apenas usaria StandardCharsets.)

Jon Skeet
fonte
2
+1. Mas como método, e não como campo, para permitir carregamento lento (ok, você provavelmente desejará UTF-8, mas existem alguns outros conjuntos de caracteres e talvez queira instalações semelhantes para eles). Infelizmente, isso não parece ser muito popular entre os que tomam as decisões.
Tom Hawtin # tackline
Eu ficaria feliz o suficiente com um método, embora eu espere que carregar ansiosamente esses poucos caracteres não seja um custo significativo.
9139 Jon Skeet
1
Estamos em uma cruzada para parar o carregamento ansioso das aulas. / Acabei de pesquisar no JDK por "UTF-8". Foram encontradas 270 correspondências em 165 arquivos. Embora muito disso esteja no antigo lixo Apache (acredito que tenha sido contribuído pela minha equipe).
Tom Hawtin # tackline
1
@tackline: Suponho que o ansioso carregamento de classes seja uma daquelas coisas que aumentam com o tempo. Algumas aulas aqui, algumas aulas ali - cada uma delas soando suficientemente inócua - podem fazer uma grande diferença.
9139 Jon Skeet
O último link, para Guava Charsets, está quebrado.
Larsh
28

No Java 1.7

import java.nio.charset.StandardCharsets

ex: StandardCharsets.UTF_8 StandardCharsets.US_ASCII

Roger
fonte
5

O estado atual da API de codificação deixa algo a desejar. Algumas partes da API Java 6 não aceito Charsetno lugar de um string (em logging, dom.ls, PrintStream, pode haver outros). Não ajuda que as codificações devam ter nomes canônicos diferentes para diferentes partes da biblioteca padrão.

Eu posso entender como as coisas chegaram onde estão; Não tenho certeza se tenho idéias brilhantes sobre como corrigi-las.


Como um aparte ...

Você pode procurar os nomes para a implementação Java 6 da Sun aqui .

Para UTF-8, os valores canônicos são "UTF-8"para java.nioe "UTF8"para java.lange java.io. As únicas codificações exigidas por uma especificação para o JRE são: US-ASCII; ISO-8859-1; UTF-8; UTF-16BE; UTF-16LE; UTF-16 .

McDowell
fonte
2
Não invejo a do PrintStream, pois a classe diz claramente "A classe PrintWriter deve ser usada em situações que exijam a gravação de caracteres em vez de bytes". (Que é, como, todas as situações ...)
Kevin Bourrillion
2

Há muito tempo, defini uma classe de utilitário com constantes UTF_8, ISO_8859_1 e US_ASCII Charset.

Além disso, alguns há muito tempo (2+ anos) eu fiz um teste de desempenho simples entre new String( byte[], Charset )e new String( byte[], String charset_name )e descobriu que esta implementação é CONSIDERAVELMENTE mais rápido. Se você der uma olhada no código fonte, verá que eles realmente seguem um caminho bem diferente.

Por esse motivo, incluí um utilitário na mesma classe

public static String stringFromByteArray (
    final byte[] array,
    final Charset charset
)
{
    try
    {
        return new String( array, charset.name( ) )
    }
    catch ( UnsupportedEncodingException ex )
    {
        // cannot happen
    }
}

Por que o construtor String (byte [], Charset) não faz o mesmo, me bate.

Alexander Pogrebnyak
fonte
1
Não Charsetprecisa ser registrado, para que a exceção possa acontecer. IIRC, houve algumas alterações no JDK7 para torná-lo mais rápido para Charsetimplementações em bom estado (eliminar a cópia extra).
Tom Hawtin - defina