Почему имена кодировок не являются константами?


211

Проблемы с кодировками сами по себе сбивают с толку и усложняют, но, кроме того, вы должны помнить точные имена ваших кодировок. Это "utf8"? Или "utf-8"? Или может быть "UTF-8"? При поиске примеров кода в интернете вы увидите все вышеперечисленное. Почему бы просто не сделать их именованными константами и использовать Charset.UTF8?


19
+1: это также постоянно беспокоило меня. MessageDigest#getInstance()Кстати, та же история продолжается .
BalusC

2
Чтобы получить реальный ответ, вам нужно спросить кого-нибудь в Sun. Удачи с этим :-)
Стивен C

1
Стивен С. Я считаю, что это обсуждалось в публичном списке рассылки. Кто-то на Солнце.
Том Хотин - tackline

Ответы:


160

Простой ответ на заданный вопрос заключается в том, что доступные строки charset варьируются от платформы к платформе.

Тем не менее, есть шесть, которые должны присутствовать, поэтому константы могли быть сделаны для тех, кто давно. Я не знаю, почему они не были.

JDK 1.4 сделал отличную вещь, представив тип Charset. На этом этапе они больше не хотели бы предоставлять строковые константы, поскольку цель состоит в том, чтобы заставить всех использовать экземпляры Charset. Так почему же тогда не предоставить шесть стандартных констант Charset? Я спросил Мартина Буххольца, так как он сидит рядом со мной, и он сказал, что не было действительно особой причины, за исключением того, что в то время все еще не получилось - слишком мало API JDK было модифицировано для принять Charset, и из тех, которые были, перегрузки Charset обычно выполнялись немного хуже.

Печально, что только в JDK 1.6 они наконец-то закончили оснащение перегрузками Charset. И что эта ситуация с производительностью в обратном направлении все еще существует (причина, почему это невероятно странно, и я не могу это объяснить, но это связано с безопасностью!).

Короче говоря - просто определите свои собственные константы или используйте класс Charsets Guava, с которым связан Тони Пони (хотя эта библиотека на самом деле еще не выпущена).

Обновление:StandardCharsets класс в JDK 7.


Просто любопытно, есть идеи, когда выйдет (альфа / бета / что угодно) Guava? Домашняя страница проекта немного ограничена.
Джоник

Нет индейки для меня, пока это не вышло!
Кевин Бурриллион

причина невероятно странная, и я не могу это объяснить, но она связана с безопасностью - вы можете создать изменяемую строку с помощью пользовательских кодировок, но они могли бы работать даже быстрее, чем строка (которая фактически ищет кодировку). Это упущение / пренебрежение тем, как String(byte bytes[], int offset, int length, Charset charset)это реализовано. На самом деле, при создании маленькой строки из большого байта совсем не просто падение производительности.
bestsss

7
Нечестно! У вас есть доступ к таким большим ресурсам. = (Я видел другой ответ, где вы однажды сказали: «Да, поэтому я спросил Джоша [Блоха] об этом ...»
kevinarpe

PrintStream не поддерживает Charset
rofrol

102

Два года спустя и StandardCharsets в Java 7 теперь определяют константы для 6 стандартных наборов символов.

Если вы застряли на Java 5/6, вы можете использовать гуавы в Charsets константы, как это было предложено Кевин Bourrillion и Джон тарелочкам.


29

Я бы сказал, что мы можем добиться гораздо большего ... почему не гарантированно доступны доступные кодировки? Charset.UTF8должна быть ссылкой на Charset, а не на имя в виде строки. Таким образом, нам не придется справлятьсяUnsupportedEncodingException повсюду.

Имейте в виду, я также думаю, что .NET выбрал лучшую стратегию, по умолчанию везде UTF-8. Затем он облажался, просто назвав свойство кодировки «по умолчанию для операционной системы» Encoding.Default- что не является значением по умолчанию в самом .NET :(

Возвращаясь к разглагольствованиям о поддержке кодировки Java - почему нет конструктора для FileWriter /, FileReaderкоторый принимает Charset? По сути, это практически бесполезные классы из-за этого ограничения - вам почти всегда нужен InputStreamReaderокругленный FileInputStreamили эквивалентный для вывода :(

Медсестра, медсестра - где мое лекарство?

РЕДАКТИРОВАТЬ: Мне приходит в голову, что это на самом деле не ответил на вопрос. Реальный ответ, по-видимому, либо «никто не думал об этом», либо «кто-то думал, что это плохая идея». Я настоятельно рекомендую, чтобы внутренние служебные классы, предоставляющие имена или кодировки, избегали дублирования вокруг кодовой базы ... Или вы могли бы просто использовать тот, который мы использовали в Google, когда этот ответ был впервые написан . (Обратите внимание, что с Java 7 вы бы просто использовали StandardCharsetsвместо этого.)


2
+1. Но как метод, а не поле, чтобы разрешить отложенную загрузку (хорошо, вы, вероятно, захотите UTF-8, но есть несколько других кодировок, и вам могут потребоваться аналогичные средства для них). К сожалению, это не очень популярно среди тех, кто принимает решения.
Том Хотин - Tackline

Я был бы достаточно счастлив с этим методом, хотя я надеюсь, что энергичная загрузка этих очень немногих кодировок не будет значительной затратой.
Джон Скит

1
Мы находимся в крестовом походе, чтобы остановить нетерпеливую загрузку классов. / Только что сделал поиск JDK для "UTF-8". Найдено 270 совпадений в 165 файлах. Хотя многое из этого находится в старом апачском барахле (я полагаю, что сделано моей командой).
Том Хотин - Tackline

1
@tackline: я полагаю, что энергичная загрузка классов - это одна из тех вещей, которая растет со временем. Несколько классов здесь, несколько классов там - каждый из которых звучит достаточно безобидно - может иметь большое значение.
Джон Скит

Последняя ссылка на Guava Charsets не работает.
LarsH

28

В Java 1.7

import java.nio.charset.StandardCharsets

например: StandardCharsets.UTF_8 StandardCharsets.US_ASCII


5

Текущее состояние API кодирования оставляет желать лучшего. Некоторые части Java 6 API не принимают Charsetвместо строки (в logging, dom.ls, PrintStream, могут быть и другие). Это не помогает, если предполагается, что кодировки имеют разные канонические имена для разных частей стандартной библиотеки.

Я могу понять, как вещи попали туда, где они есть; не уверен, что у меня есть блестящие идеи о том, как их исправить.


Как в сторону...

Вы можете посмотреть имена для реализации Java 6 Sun здесь .

Для UTF-8 канонические значения предназначены "UTF-8"для java.nioи "UTF8"для java.langи java.io. Единственные кодировки, которые спецификация требует для поддержки JRE: US-ASCII; ISO-8859-1; UTF-8; UTF-16BE; UTF-16LE; UTF-16 .


2
Я не завладею PrintStream, поскольку класс четко говорит: «Класс PrintWriter должен использоваться в ситуациях, когда требуется написание символов, а не байтов». (Что, как и все ситуации ...)
Кевин Бурриллион

2

Я давно определил служебный класс с константами Charset UTF_8, ISO_8859_1 и US_ASCII.

Кроме того , некоторые давно ( от 2 лет) , я сделал простой тест производительности между new String( byte[], Charset )и new String( byte[], String charset_name )и обнаружил , что последняя реализация ЗНАЧИТЕЛЬНО быстрее. Если вы посмотрите изнутри на исходный код, вы увидите, что он действительно следует совершенно другим путем.

По этой причине я включил утилиту в том же классе

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

Почему конструктор String (byte [], Charset) не делает то же самое, лучше меня.


1
Не Charsetнужно регистрироваться, поэтому исключение может произойти. IIRC, в JDK7 были внесены некоторые изменения, чтобы сделать его быстрее для хорошо известных Charsetреализаций (исключите лишнюю копию).
Том Хотин - tackline
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.