Warum sind Zeichensatznamen keine Konstanten?

211

Zeichensatzprobleme sind für sich genommen verwirrend und kompliziert, aber darüber hinaus müssen Sie sich die genauen Namen Ihrer Zeichensätze merken. Ist es "utf8"? Oder "utf-8"? Oder vielleicht "UTF-8"? Wenn Sie im Internet nach Codebeispielen suchen, sehen Sie alle oben genannten Punkte. Warum nicht einfach Konstanten benennen und verwenden Charset.UTF8?

serg
quelle
19
+1: Das hat mich auch die ganze Zeit nervt. Die gleiche Geschichte geht MessageDigest#getInstance()übrigens weiter.
BalusC
2
Für die richtige Antwort müssten Sie jemanden bei Sun fragen. Viel Glück damit :-)
Stephen C
1
Stephen C: Ich glaube, es wurde auf einer öffentlichen Mailingliste diskutiert. -Jemand bei Sun.
Tom Hawtin - Tackline
1
siehe diese Frage
yegor256

Antworten:

160

Die einfache Antwort auf die gestellte Frage lautet, dass die verfügbaren Zeichensatzzeichenfolgen von Plattform zu Plattform variieren.

Es müssen jedoch sechs vorhanden sein, sodass Konstanten für diese vor langer Zeit erstellt werden könnten. Ich weiß nicht, warum sie es nicht waren.

JDK 1.4 hat mit der Einführung des Charset-Typs großartige Arbeit geleistet. Zu diesem Zeitpunkt hätten sie keine String-Konstanten mehr bereitstellen wollen, da das Ziel darin besteht, alle Benutzer dazu zu bringen, Charset-Instanzen zu verwenden. Warum also nicht die sechs Standard-Zeichensatzkonstanten bereitstellen? Ich habe Martin Buchholz gefragt, da er zufällig direkt neben mir sitzt, und er sagte, es gebe keinen wirklich besonders guten Grund, außer dass die Dinge zu diesem Zeitpunkt noch halbherzig waren - zu wenige JDK-APIs wurden nachgerüstet Akzeptieren Sie Charset, und von denen, die es waren, zeigten die Charset-Überladungen normalerweise etwas schlechtere Ergebnisse.

Es ist traurig, dass sie erst in JDK 1.6 endlich alles mit Charset-Überladungen ausgestattet haben. Und dass diese rückwärts gerichtete Leistungssituation immer noch besteht (der Grund dafür ist unglaublich seltsam und ich kann es nicht erklären, hängt aber mit der Sicherheit zusammen!).

Lange Rede, kurzer Sinn - definieren Sie einfach Ihre eigenen Konstanten oder verwenden Sie die Charsets-Klasse von Guava, mit der Tony the Pony verknüpft ist (obwohl diese Bibliothek noch nicht wirklich veröffentlicht wurde).

Update: Eine StandardCharsetsKlasse befindet sich in JDK 7.

Kevin Bourrillion
quelle
Nur neugierig, eine Idee, wann es eine Veröffentlichung (Alpha / Beta / was auch immer) von Guava geben wird? Die Projekthomepage ist diesbezüglich etwas knapp.
Jonik
Kein Truthahn für mich, bis es raus ist!
Kevin Bourrillion
Der Grund dafür ist unglaublich seltsam und ich kann es nicht erklären, hängt aber mit der Sicherheit zusammen - Sie können einen modifizierbaren String über benutzerdefinierte Zeichensätze erstellen, aber sie könnten sogar schneller funktionieren als ein String (der den Zeichensatz tatsächlich nachschlägt). Es ist eine Auslassung / Vernachlässigung, wie String(byte bytes[], int offset, int length, Charset charset)implementiert wird. Tatsächlich ist der Leistungstreffer beim Erstellen einer kleinen Zeichenfolge aus einem großen Byte [] überhaupt nicht trivial.
Bests
7
Keine Messe! Sie haben Zugriff auf so großartige Ressourcen. = (Ich habe eine andere Antwort gesehen, in der Sie einmal gesagt haben: "Ja, also habe ich Josh [Bloch] danach gefragt ..."
Kevinarpe
PrintStream unterstützt Charset
rofrol
102

Zwei Jahre später definieren die StandardCharsets von Java 7 nun Konstanten für die 6 Standardzeichensätze.

Wenn Sie nicht mit Java 5/6 arbeiten, können Sie die Charavets- Konstanten von Guava verwenden , wie von Kevin Bourrillion und Jon Skeet vorgeschlagen.

Etienne Neveu
quelle
29

Ich würde argumentieren, dass wir es viel besser machen können ... warum sind die garantiert verfügbaren Zeichensätze nicht direkt zugänglich? Charset.UTF8sollte ein Verweis auf die sein Charset, nicht der Name als Zeichenfolge. Auf diese Weise müssten wir nicht UnsupportedEncodingExceptionüberall damit umgehen .

Wohlgemerkt, ich denke auch, dass .NET eine bessere Strategie gewählt hat, indem es überall standardmäßig UTF-8 verwendet. Es wurde dann vermasselt, indem die Codierungseigenschaft "Betriebssystemstandard" einfach benannt wurde Encoding.Default- was in .NET selbst nicht der Standard ist :(

Zurück zu Javans Zeichensatzunterstützung - warum gibt es keinen Konstruktor für FileWriter/ FileReaderder ein benötigt Charset? Grundsätzlich sind dies aufgrund dieser Einschränkung fast nutzlose Klassen - Sie benötigen fast immer eine InputStreamReaderum ein FileInputStreamoder das Äquivalent für die Ausgabe :(

Krankenschwester, Krankenschwester - wo ist meine Medizin?

EDIT: Mir fällt ein, dass dies die Frage nicht wirklich beantwortet hat. Die eigentliche Antwort lautet vermutlich entweder "niemand hat daran gedacht" oder "jemand hat gedacht, es sei eine schlechte Idee". Ich würde dringend empfehlen, dass interne Dienstprogrammklassen, die die Namen oder Zeichensätze bereitstellen, Doppelungen in der Codebasis vermeiden ... Oder Sie können einfach die verwenden, die wir bei Google verwendet haben, als diese Antwort zum ersten Mal geschrieben wurde . (Beachten Sie, dass Sie ab Java 7 nur noch verwenden würden StandardCharsets.)

Jon Skeet
quelle
2
+1. Aber als Methode und nicht als Feld, um ein verzögertes Laden zu ermöglichen (okay, Sie werden wahrscheinlich UTF-8 wollen, aber es gibt ein paar andere Zeichensätze, und Sie möchten vielleicht ähnliche Funktionen für sie). Leider scheint dies bei den Entscheidungsträgern nicht sehr beliebt zu sein.
Tom Hawtin - Tackline
Ich würde mit einer Methode zufrieden sein, obwohl ich hoffe, dass das eifrige Laden dieser wenigen Zeichensätze keine nennenswerten Kosten verursacht.
Jon Skeet
1
Wir sind auf einem Kreuzzug, um das eifrige Laden von Klassen zu stoppen. / Habe gerade ein JDK nach "UTF-8" durchsucht. 270 Übereinstimmungen in 165 Dateien gefunden. Obwohl vieles davon in altem Apache-Müll steckt (ich glaube, mein Team hat dazu beigetragen).
Tom Hawtin - Tackline
1
@tackline: Ich nehme an, eifriges Laden von Klassen ist eines der Dinge, die im Laufe der Zeit zunehmen. Ein paar Klassen hier, ein paar Klassen dort - jede klingt einzeln harmlos genug - könnten einen großen Unterschied machen.
Jon Skeet
Die letzte Verbindung zu Guava Charsets ist unterbrochen.
LarsH
28

In Java 1.7

import java.nio.charset.StandardCharsets

Ex: StandardCharsets.UTF_8 StandardCharsets.US_ASCII

Roger
quelle
5

Der aktuelle Status der Codierungs-API lässt zu wünschen übrig. Einige Teile der Java 6 API nicht akzeptieren , Charsetanstelle einer Zeichenkette (in logging, dom.ls, PrintStream, es könnte noch andere geben). Es hilft nicht, dass Codierungen unterschiedliche kanonische Namen für verschiedene Teile der Standardbibliothek haben sollen.

Ich kann verstehen, wie die Dinge dahin kamen, wo sie sind; Ich bin mir nicht sicher, ob ich brillante Ideen habe, wie ich sie beheben kann.


Nebenbei...

Sie können die Namen für Suns Java 6 Implementierung sehen hier .

Für UTF-8 sind die kanonischen Werte "UTF-8"für java.niound "UTF8"für java.langund java.io. Die einzigen Codierungen, für deren Unterstützung eine JRE erforderlich ist, sind: US-ASCII; ISO-8859-1; UTF-8; UTF-16BE; UTF-16LE; UTF-16 .

McDowell
quelle
2
Ich gönne mir den PrintStream nicht, da die Klasse klar sagt: "Die PrintWriter-Klasse sollte in Situationen verwendet werden, in denen eher Zeichen als Bytes geschrieben werden müssen." (Das sind wie alle Situationen ...)
Kevin Bourrillion
2

Ich habe vor langer Zeit eine Utility-Klasse mit den Zeichensatzkonstanten UTF_8, ISO_8859_1 und US_ASCII definiert.

Außerdem habe ich vor einiger Zeit (2+ Jahre) einen einfachen Leistungstest zwischen new String( byte[], Charset )und durchgeführt new String( byte[], String charset_name )und festgestellt, dass die letztere Implementierung erheblich schneller ist. Wenn Sie sich den Quellcode unter der Haube ansehen, werden Sie feststellen, dass sie tatsächlich einen ganz anderen Weg einschlagen.

Aus diesem Grund habe ich ein Dienstprogramm in dieselbe Klasse aufgenommen

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

Warum der String-Konstruktor (byte [], Charset) nicht dasselbe tut, ist mir ein Rätsel.

Alexander Pogrebnyak
quelle
1
Die Charsetmüssen nicht registriert werden, daher kann die Ausnahme auftreten. IIRC, es gab einige Änderungen in JDK7, um es für bekanntermaßen gute CharsetImplementierungen schneller zu machen (entfernen Sie die zusätzliche Kopie).
Tom Hawtin - Tackline