Dlaczego nazwy zestawów znaków nie są stałymi?


211

Kwestie zestawów znaków są same w sobie mylące i skomplikowane, ale ponadto należy pamiętać dokładne nazwy zestawów znaków. Czy to "utf8"jest Czy "utf-8"? A może "UTF-8"? Podczas wyszukiwania przykładów kodu w Internecie zobaczysz wszystkie powyższe. Dlaczego nie nadać im nazw stałych i używać Charset.UTF8?


19
+1: Cały czas mnie to również wkurzało. Nawiasem MessageDigest#getInstance()mówiąc, ta sama historia trwa .
BalusC,

2
Aby uzyskać prawdziwą odpowiedź, musisz zapytać kogoś w Sun. Powodzenia z tym :-)
Stephen C

1
Stephen C: Myślę, że zostało to omówione na publicznej liście mailingowej. -Ktoś w Sun.
Tom Hawtin - tackline

1

Odpowiedzi:


160

Prosta odpowiedź na zadane pytanie jest taka, że ​​dostępne ciągi znaków zestawu znaków różnią się w zależności od platformy.

Jednak istnieje sześć, które muszą być obecne, więc dla tych dawnych można było stworzyć stałe. Nie wiem, czemu nie byli.

JDK 1.4 zrobił świetną rzecz, wprowadzając typ Charset. W tym momencie nie chcieliby już podawać stałych String, ponieważ celem jest, aby wszyscy używali instancji Charset. Dlaczego więc nie podać sześciu standardowych stałych Charseta? Zapytałem Martina Buchholza, ponieważ akurat siedział obok mnie, i powiedział, że nie ma naprawdę szczególnie dobrego powodu, z wyjątkiem tego, że w tamtym czasie wszystko było nadal w połowie upieczone - zbyt mało API JDK zostało zmodernizowanych do zaakceptuj Charset, a spośród nich przeciążenia Charset zwykle działały nieco gorzej.

To smutne, że dopiero w JDK 1.6 w końcu skończyli wyposażać wszystko w przeciążenia Charset. I że ta sytuacja dotycząca wydajności wstecznej nadal istnieje (powód, dla którego jest niesamowicie dziwny i nie mogę tego wyjaśnić, ale jest związany z bezpieczeństwem!).

Krótko mówiąc - po prostu zdefiniuj własne stałe lub skorzystaj z klasy Charsets Guava, z którą łączył się Tony Pony (choć ta biblioteka nie jest jeszcze tak naprawdę wydana).

Aktualizacja:StandardCharsets klasa jest w JDK 7.


Ciekawe, jakiś pomysł, kiedy pojawi się wydanie (alfa / beta / cokolwiek) Guava? Strona główna projektu jest nieco krótka.
Jonik,

Nie ma dla mnie indyka, dopóki nie wyjdzie!
Kevin Bourrillion

powód, dla którego jest niesamowicie dziwny i nie potrafię tego wyjaśnić, ale jest związany z bezpieczeństwem - możesz utworzyć modyfikowalny ciąg znaków za pomocą niestandardowych zestawów znaków, ale mogły one sprawić, że będą działały nawet szybciej niż łańcuch znaków (który faktycznie wyszukuje zestaw znaków). Pominięcie / zaniedbanie sposobu String(byte bytes[], int offset, int length, Charset charset)implementacji. W rzeczywistości uderzenie wydajności wcale nie jest trywialne przy tworzeniu małego ciągu z dużego bajtu [].
bestsss

7
Nie fair! Masz dostęp do tak wspaniałych zasobów. = (Zobaczyłem inną odpowiedź, w której powiedziałeś kiedyś: „Tak, więc zapytałem o to Josha [Blocha] ...”
kevinarpe,

PrintStream nie obsługuje Charset
rofrol

102

Dwa lata później StandardCharset Java 7 definiuje teraz stałe dla 6 standardowych zestawów znaków.

Jeśli utkniesz na Javie 5/6, możesz użyć stałych znaków Guava , jak sugerują Kevin Bourrillion i Jon Skeet.


29

Twierdziłbym, że możemy zrobić znacznie więcej niż to ... dlaczego gwarantowane dostępność zestawów znaków nie jest dostępne bezpośrednio? Charset.UTF8powinno być odniesieniem do Charset, a nie nazwą jako ciągiem znaków. W ten sposób nie musielibyśmy radzić sobie w UnsupportedEncodingExceptionkażdym miejscu.

Pamiętaj, myślę również, że .NET wybrał lepszą strategię, domyślnie wszędzie używając UTF-8. Następnie spieprzył, nazywając po prostu właściwość kodowania „domyślny system operacyjny” Encoding.Default- co nie jest wartością domyślną w samym .NET :(

Wróć do rantingu na temat obsługi zestawu znaków Java - dlaczego nie ma konstruktora dla FileWriter/ FileReaderktóry bierze Charset? Zasadniczo są to prawie bezużyteczne klasy z powodu tego ograniczenia - prawie zawsze potrzebujesz InputStreamReaderokołoFileInputStream lub odpowiednika dla danych wyjściowych :(

Pielęgniarka, pielęgniarka - gdzie jest moje lekarstwo?

EDYCJA: Przyszło mi do głowy, że tak naprawdę nie odpowiedziałem na pytanie. Prawdziwą odpowiedzią jest prawdopodobnie „nikt nie pomyślał o tym” lub „ktoś zaangażowany uważał, że to zły pomysł”. Zdecydowanie sugerowałbym, aby wewnętrzne klasy narzędziowe zawierające nazwy lub zestawy znaków unikały powielania wokół bazy kodu ... Albo możesz po prostu użyć tej, której używaliśmy w Google, kiedy ta odpowiedź była napisana po raz pierwszy . (Należy pamiętać, że od wersji Java 7 wystarczy użyć StandardCharsetszamiast tego).


2
+1. Ale jako metoda, a nie pole, aby umożliwić leniwe ładowanie (dobrze, prawdopodobnie będziesz chciał UTF-8, ale istnieje kilka innych zestawów znaków i możesz chcieć dla nich podobnych udogodnień). Niestety nie wydaje się to zbyt popularne wśród osób podejmujących decyzje.
Tom Hawtin - tackline

Byłbym wystarczająco zadowolony z tej metody, chociaż mam nadzieję, że niecierpliwie załadowanie tak niewielu zestawów znaków nie będzie znaczącym kosztem.
Jon Skeet

1
Jesteśmy w krucjacie, aby powstrzymać chętne ładowanie klas. / Właśnie przeszukałem JDK dla „UTF-8”. Znaleziono 270 pasujących elementów w 165 plikach. Chociaż wiele z nich znajduje się w starych śmieciach Apache (uważam, że przyczyniło się to przez mój zespół).
Tom Hawtin - tackline

1
@tackline: Przypuszczam, że chętne ładowanie klas jest jedną z tych rzeczy, które rosną z czasem. Kilka klas tutaj, kilka klas - każda z osobna brzmi wystarczająco niewinnie - może mieć duże znaczenie.
Jon Skeet,

Ostatni link do Guava Charsets jest zepsuty.
LarsH

28

W Javie 1.7

import java.nio.charset.StandardCharsets

dawny: StandardCharsets.UTF_8 StandardCharsets.US_ASCII


5

Obecny stan API kodowania pozostawia wiele do życzenia. Niektóre części Java 6 API nie akceptują Charsetzamiast łańcucha (w logging, dom.ls, PrintStream, nie mogą być inne). Nie pomaga to, że kodowania mają różne nazwy kanoniczne dla różnych części standardowej biblioteki.

Rozumiem, jak rzeczy dotarły tam, gdzie są; nie jestem pewien, czy mam jakieś genialne pomysły, jak je naprawić.


Tak na marginesie...

Można spojrzeć na nazwy dla Java 6 wdrażania Sun tutaj .

W przypadku UTF-8 wartości kanoniczne są "UTF-8"dla java.nioi "UTF8"dla java.langijava.io . Jedyne kodowania, których specyfikacja wymaga środowiska JRE, to: US-ASCII; ISO-8859-1; UTF-8; UTF-16BE; UTF-16LE; UTF-16 .


2
Nie żałuję jednego z PrintStream, ponieważ klasa wyraźnie mówi „Klasa PrintWriter powinna być używana w sytuacjach, które wymagają pisania znaków, a nie bajtów”. (Które są jak wszystkie sytuacje ...)
Kevin Bourrillion

2

Dawno temu zdefiniowałem klasę użyteczności ze stałymi zestawami znaków UTF_8, ISO_8859_1 i US_ASCII.

Również niektóre dawno temu (2+ lat) zrobiłem prosty test wydajności pomiędzy new String( byte[], Charset )a new String( byte[], String charset_name )i odkrył, że ta ostatnia realizacja jest ZNACZNIE szybciej. Jeśli spojrzysz pod maską na kod źródłowy, zobaczysz, że rzeczywiście podążają zupełnie inną ścieżką.

Z tego powodu dołączyłem narzędzie do tej samej klasy

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

Dlaczego konstruktor String (bajt [], Charset) nie robi tego samego, bije mnie.


1
Nie Charsettrzeba rejestrować, więc może się zdarzyć wyjątek. IIRC, wprowadzono pewne zmiany w JDK7, aby przyspieszyć dla znanych dobrych Charsetimplementacji (wyeliminować dodatkową kopię).
Tom Hawtin - tackline
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.