Grundlagen des Zeichensatzes:
Zeichensatz
Eine Sammlung von Zeichen, also Symbolen mit besonderer Semantik. Der Buchstabe „A“ ist ein Zeichen. „%“ ist ebenfalls ein Zeichen. Es hat keinen intrinsischen numerischen Wert und keine direkte Verbindung zu ASC II, Unicode oder sogar Computern. Symbole gab es schon lange vor Computern.
Codierter Zeichensatz
Einer Sammlung von Zeichen wird ein numerischer Wert zugewiesen. Weisen Sie Zeichen Codes zu, damit diese mithilfe eines bestimmten Zeichenkodierungssatzes numerische Ergebnisse ausdrücken können. Andere codierte Zeichensätze können demselben Zeichen unterschiedliche Werte zuweisen. Zeichensatzzuordnungen werden normalerweise von Standardorganisationen wie USASCII, ISO 8859-1, Unicode (ISO 10646-1) und JIS X0201 festgelegt.
Zeichenkodierungsschema
Zuordnung codierter Zeichensatzmitglieder zu Oktetten (8-Bit-Bytes). Ein Kodierungsschema definiert, wie eine Folge von Zeichenkodierungen als Folge von Bytes ausgedrückt wird. Der Wert der Zeichenkodierung muss nicht mit dem Kodierungsbyte identisch sein und es muss auch keine Eins-zu-eins- oder Eins-zu-viele-Beziehung vorliegen. Im Prinzip kann die Kodierung und Dekodierung von Zeichensätzen als Objektserialisierung und -deserialisierung angenähert werden.
Normalerweise wird die Zeichendatenkodierung für die Netzwerkübertragung oder Dateispeicherung verwendet. Ein Codierungsschema ist kein Zeichensatz, sondern eine Zuordnung. Aufgrund ihrer engen Beziehung sind die meisten Codierungen jedoch einem separaten Zeichensatz zugeordnet. Zum Beispiel UTF-8,
Wird nur zum Kodieren von Unicode-Zeichensätzen verwendet. Dennoch ist es möglich, ein Codierungsschema für die Verarbeitung mehrerer Zeichensätze zu verwenden. EUC kann beispielsweise Zeichen für mehrere asiatische Sprachen kodieren.
Abbildung 6-1 ist ein grafischer Ausdruck, der das UTF-8-Kodierungsschema verwendet, um eine Unicode-Zeichenfolge in eine Bytefolge zu kodieren. UTF-8 kodiert Zeichencodewerte kleiner als 0x80 in einen Einzelbyte-Wert (Standard ASC II). Alle anderen Unicode-Zeichen werden als Multibyte-Sequenzen von 2 bis 6 Bytes codiert (http://www.ietf.org/rfc/rfc2279.txt).
Zeichensatz
Der Begriff Zeichensatz ist in RFC2278 (http://ietf.org/rfc/rfc2278.txt) definiert. Es handelt sich um eine Sammlung codierter Zeichensätze und Zeichencodierungsschemata. Die Klasse des Pakets java.nio.charset ist Charset, das die Zeichensatzextraktion kapselt.
1111111111111111
Unicode ist eine 16-Bit-Zeichenkodierung. Es versucht, die Zeichensätze aller Sprachen der Welt in einer einzigen, umfassenden Zuordnung zu vereinen. Es hat sich seinen Platz verdient, aber es gibt heute noch viele andere Zeichenkodierungen, die weit verbreitet sind.
Die meisten Betriebssysteme sind immer noch byteorientiert in Bezug auf E/A und Dateispeicherung. Daher besteht unabhängig von der verwendeten Kodierung, Unicode oder anderen Kodierungen, immer noch die Notwendigkeit, zwischen Bytesequenzen und Zeichensatzkodierungen zu konvertieren.
Die aus dem Paket java.nio.charset bestehenden Klassen erfüllen diesen Bedarf. Dies ist nicht das erste Mal, dass sich die Java-Plattform mit der Zeichensatzkodierung befasst, aber es ist die systematischste, umfassendste und flexibelste Lösung. Das Paket java.nio.charset.spi stellt eine Server Provisioning Interface (SPI) bereit, sodass Encoder und Decoder nach Bedarf angeschlossen werden können.
Zeichensatz: Der Standardwert wird beim JVM-Start bestimmt und hängt von der zugrunde liegenden Betriebssystemumgebung, dem Gebietsschema und/oder der JVM-Konfiguration ab. Wenn Sie einen bestimmten Zeichensatz benötigen, ist es am sichersten, ihn explizit zu benennen. Gehen Sie nicht davon aus, dass die Standardbereitstellung mit Ihrer Entwicklungsumgebung übereinstimmt. Bei Zeichensatznamen wird die Groß- und Kleinschreibung nicht beachtet, d. h. Groß- und Kleinbuchstaben werden beim Vergleich von Zeichensatznamen als gleich betrachtet. Die Internet Assigned Names Authority (IANA) verwaltet alle offiziell registrierten Zeichensatznamen.
Beispiel 6-1 zeigt, wie Zeichen mithilfe verschiedener Charset-Implementierungen in Bytesequenzen übersetzt werden.
Beispiel 6-1. Verwendung der Standardzeichensatzkodierung
Paket com.ronsoft.books.nio.charset;
import java.nio.charset.Charset;
import java.nio.ByteBuffer;
/**
* Zeichensatz-Codierungstest. Führen Sie dieselbe Eingabezeichenfolge aus, die einige enthält
* Nicht-ASCII-Zeichen, über mehrere Charset-Encoder und geben Sie das Hex aus
* Werte der resultierenden Bytesequenzen.
*
* @Autor Ron Hitchens ([email protected])
*/
öffentliche Klasse EncodeTest {
public static void main(String[] argv) löst eine Ausnahme aus {
// Dies ist die zu kodierende Zeichenfolge
String input = "/u00bfMa/u00f1ana?";
// die Liste der Zeichensätze, mit denen codiert werden soll
String[] charsetNames = { "US-ASCII", "ISO-8859-1", "UTF-8",
„UTF-16BE“, „UTF-16LE“, „UTF-16“ // , „X-ROT13“
};
for (int i = 0; i < charsetNames.length; i++) {
doEncode(Charset.forName(charsetNames[i]), input);
}
}
/**
* Für einen bestimmten Zeichensatz und eine Eingabezeichenfolge kodieren Sie die Zeichen und drucken Sie sie aus
* resultierende Byte-Kodierung in lesbarer Form.
*/
private static void doEncode(Charset cs, String input) {
ByteBuffer bb = cs.encode(input);
System.out.println("Charset: " + cs.name());
System.out.println(" Eingabe: " + Eingabe);
System.out.println("Encoded: ");
for (int i = 0; bb.hasRemaining(); i++) {
int b = bb.get();
int ival = ((int) b) & 0xff;
char c = (char) ival;
// Tabellenausrichtung hübsch halten
wenn (i < 10)
System.out.print(" ");
//Indexnummer drucken
System.out.print(" " + i + ": ");
// Eines Tages kommt eine besser formatierte Ausgabe ...
if (ival < 16)
System.out.print("0");
// Den Hexadezimalwert des Bytes ausgeben
System.out.print(Integer.toHexString(ival));
// Wenn das Byte der Wert von a zu sein scheint
// druckbares Zeichen, drucken Sie es aus. Keine Garantie
// es wird sein.
if (Character.isWhitespace(c) || Character.isISOControl(c)) {
System.out.println("");
} anders {
System.out.println(" (" + c + ")");
}
}
System.out.println("");
}
}
Zeichensatz: ISO-8859-1
Eingabe: ?Ma?ana?
Codiert:
0:20
1: bf (?)
2: 4d (M)
3:61(a)
4: f1 (?)
5:61(a)
6: 6e(n)
7:61(a)
8: 3f (?)
Zeichensatz: UTF-8
Eingabe: ?Ma?ana?
Codiert:
0:20
1: c2 (?)
2: bf (?)
3: 4d (M)
4:61(a)
5: c3 (?)
6: b1 (±)
7:61(a)
8: 6e(n)
9:61(a)
10: 3f (?)
Zeichensatz: UTF-16BE
Eingabe: ?Ma?ana?
Codiert:
0:00
1:20
2:00
3: bf (?)
4:00
5: 4d (M)
6:00
7:61(a)
8:00
9: f1 (?)
10:00
11:61(a)
12:00
13: 6e(n)
14:00
15: 61 (a)
16:00
17: 3f (?)
Zeichensatz: UTF-16LE
Eingabe: ?Ma?ana?
Codiert:
0:20
1:00
2: bf (?)
3:00
4: 4d (M)
5:00
6:61(a)
7:00
8: f1 (?)
9:00
10:61(a)
11:00
12: 6e(n)
13:00
14: 61 (a)
15:00
16: 3f (?)
17:00
Zeichensatz: UTF-16
Eingabe: ?Ma?ana?
Codiert:
0: fe (?)
1: ff (?)
2:00
3:20
4:00
5: bf (?)
6:00
7: 4d (M)
8:00
9:61(a)
10:00
11: f1 (?)
12:00
13: 61 (a)
14:00
15: 6e(n)
16:00
17: 61 (a)
18:00
19: 3f (?)
Paket java.nio.charset;
Die öffentliche abstrakte Klasse Charset implementiert Comparable
{
öffentlicher statischer boolescher Wert isSupported (String charsetName)
öffentlicher statischer Zeichensatz forName (String charsetName)
öffentliche statische SortedMap availableCharsets()
öffentlicher finaler Stringname()
public final Set aliases()
öffentlicher String displayName()
public String displayName (Gebietsschema)
öffentlicher finaler boolescher Wert isRegistered()
öffentlicher boolescher Wert canEncode()
öffentlicher abstrakter CharsetEncoder newEncoder();
öffentliche endgültige ByteBuffer-Kodierung (CharBuffer cb)
öffentliche endgültige ByteBuffer-Kodierung (String str)
öffentlicher abstrakter CharsetDecoder newDecoder();
öffentliche endgültige CharBuffer-Dekodierung (ByteBuffer bb)
öffentlicher abstrakter boolescher Wert enthält (Charset cs);
public final boolean equals (Objekt ob)
public final int vergleichenTo (Objekt ob)
public final int hashCode()
öffentlicher finaler String toString()
}
Meistens beachten nur JVM-Verkäufer diese Regeln. Wenn Sie jedoch planen, Ihren eigenen Zeichensatz als Teil Ihrer Anwendung zu verwenden, ist es hilfreich zu wissen, was Sie nicht tun sollten. Sie sollten für isRegistered() false zurückgeben und Ihren Zeichensatz beginnend mit „X -“ benennen.
Zeichensatzvergleich:
Die öffentliche abstrakte Klasse Charset implementiert Comparable
{
// Dies ist eine teilweise API-Auflistung
öffentlicher abstrakter boolescher Wert enthält (Charset cs);
public final boolean equals (Objekt ob)
public final int vergleichenTo (Objekt ob)
public final int hashCode()
öffentlicher finaler String toString()
}
Zeichensatz-Encoder: Ein Zeichensatz besteht aus einem codierten Zeichensatz und einem zugehörigen Codierungsschema. Die Klassen CharsetEncoder und CharsetDecoder implementieren Konvertierungsschemata.
Ein Hinweis zur CharsetEncoder-API: Erstens: Je einfacher die encode()-Form, desto praktischer ist sie. Die Codierung des CharBuffers, die Sie im neu zugewiesenen ByteBuffer bereitstellen, kombiniert alle Codierungen. Dies ist die letzte Methode, die aufgerufen wird, wenn Sie encode() direkt für die Charset-Klasse aufrufen.
Unterlauf
Überlauf
Fehlerhafte Eingabe
Nicht zuordenbarer Charakter
Wenn der Encoder beim Codieren auf fehlerhafte oder nicht zuordenbare Eingaben stößt, wird ein Ergebnisobjekt zurückgegeben. Sie können auch einzelne Zeichen oder Zeichenfolgen testen, um festzustellen, ob sie codiert werden können. So prüfen Sie, ob eine Kodierung möglich ist:
Paket java.nio.charset;
öffentliche abstrakte Klasse CharsetEncoder
{
// Dies ist eine teilweise API-Auflistung
öffentlicher boolescher canEncode (char c)
öffentlicher boolescher canEncode (CharSequence cs)
}
BERICHT
Standardverhalten beim Erstellen eines CharsetEncoder. Dieses Verhalten weist darauf hin, dass Codierungsfehler durch die Rückgabe des zuvor erwähnten CoderResult-Objekts gemeldet werden sollten.
IGNORE (ignorieren)
Gibt an, dass Codierungsfehler ignoriert werden sollen und jede falsche Eingabe abgebrochen werden soll, wenn sie nicht in der richtigen Position ist.
ERSETZEN
Codierungsfehler werden behandelt, indem die Eingabe des Fehlers abgebrochen und die aktuelle Ersatzbytesequenz ausgegeben wird, die für diesen CharsetEncoder definiert ist.
Denken Sie daran, dass bei der Zeichensatzkodierung Zeichen in eine Bytefolge umgewandelt werden, um sie auf die spätere Dekodierung vorzubereiten. Wenn die Ersatzsequenz nicht in eine gültige Zeichenfolge dekodiert werden kann, wird die kodierte Bytefolge ungültig.
CoderResult-Klasse: CoderResult-Objekte werden von CharsetEncoder- und CharsetDecoder-Objekten zurückgegeben:
Paket java.nio.charset;
öffentliche Klasse CoderResult {
öffentliches statisches finales CoderResult OVERFLOW
öffentliches statisches finales CoderResult UNDERFLOW
öffentlicher boolescher Wert isUnderflow()
öffentlicher boolescher isOverflow()
<span style="white-space:pre"> </span>public boolean isError()
öffentlicher boolescher Wert isMalformed()
öffentlicher boolescher Wert isUnmappable()
öffentliche int-Länge()
öffentliches statisches CoderResult malformedForLength (int-Länge)
öffentliches statisches CoderResult unmappableForLength (int-Länge)
<span style="white-space:pre"> </span>public void throwException() löst eine CharacterCodingException aus
}
Paket java.nio.charset;
öffentliche abstrakte Klasse CharsetDecoder
{
// Dies ist eine teilweise API-Auflistung
public final CharsetDecoder reset()
öffentliche endgültige CharBuffer-Dekodierung (ByteBuffer in)
löst eine CharacterCodingException aus
öffentliche endgültige CoderResult-Dekodierung (ByteBuffer in, CharBuffer out,
boolean endOfInput)
öffentlicher finaler CoderResult-Flush (CharBuffer out)
}
1. Setzen Sie den Decoder zurück, indem Sie reset() aufrufen, um den Decoder in einen bekannten Zustand zu versetzen, in dem er Eingaben empfangen kann.
2. Setzen Sie endOfInput auf false und rufen Sie decode() nicht mehrmals auf, um Bytes an die Decodierungs-Engine bereitzustellen. Mit fortschreitender Dekodierung werden Zeichen zum angegebenen CharBuffer hinzugefügt.
3. Setzen Sie endOfInput auf true und rufen Sie decode() einmal auf, um den Decoder darüber zu informieren, dass alle Eingaben bereitgestellt wurden.
4. Rufen Sie „flush()“ auf, um sicherzustellen, dass alle dekodierten Zeichen an die Ausgabe gesendet wurden.
Beispiel 6-2 zeigt, wie ein Bytestrom codiert wird, der eine Zeichensatzcodierung darstellt.
Beispiel 6-2. Zeichensatzdekodierung
Paket com.ronsoft.books.nio.charset;
java.nio.* importieren;
import java.nio.charset.*;
import java.nio.channels.*;
java.io.* importieren;
/**
* Zeichensatzdekodierung testen.
*
* @Autor Ron Hitchens ([email protected])
*/
öffentliche Klasse CharsetDecode {
/**
* Testen Sie die Zeichensatzdekodierung im allgemeinen Fall, indem Sie Puffer erkennen und verarbeiten
* Unter-/Überlauf und Löschen des Decoderstatus am Ende der Eingabe dieses Codes
* liest von stdin und dekodiert den ASCII-codierten Bytestrom in Zeichen
* dekodierte Zeichen werden nach stdout geschrieben. Dies ist praktisch eine „Katze“ für
* Geben Sie ASCII-Dateien ein, aber eine andere Zeichensatzkodierung könnte einfach verwendet werden
* Geben Sie es in der Befehlszeile an.
*/
public static void main(String[] argv) löst eine IOException {
// Der Standardzeichensatz ist Standard-ASCII
String charsetName = "ISO-8859-1";
// Der Zeichensatzname kann in der Befehlszeile angegeben werden
if (argv. Länge > 0) {
charsetName = argv[0];
}
// Einen Kanal um stdin wickeln, einen Kanal um stdout wickeln,
// den benannten Zeichensatz finden und ihn an die Methode deco de übergeben.
// Wenn der benannte Zeichensatz ungültig ist, eine Ausnahme vom Typ
// UnsupportedCharsetException wird geworfen.
decodeChannel(Channels.newChannel(System.in), new OutputStreamWriter(
System.out), Charset.forName(charsetName));
}
/**
* Statische Allzweckmethode, die Bytes aus einem Kanal liest und dekodiert
* ihnen entsprechend
*
* @param-Quelle
* Ein ReadableByteChannel-Objekt, das in EOF als gelesen wird
* Quelle der codierten Bytes.
* @param Autor
* Ein Writer-Objekt, in das dekodierte Zeichen geschrieben werden.
* @param Zeichensatz
* Ein Charset-Objekt, dessen CharsetDecoder dazu verwendet wird
* Zeichensatzdekodierung
*/
public static void decodeChannel(ReadableByteChannel Quelle, Writer Writer,
Charset (Zeichensatz) wirft UnsupportedCharsetException, IOException {
// Holen Sie sich eine Decoder-Instanz aus dem Zeichensatz
CharsetDecoder decoder = charset.newDecoder();
// Den Decoder anweisen, fehlerhafte Zeichen durch die Standardmarkierung zu ersetzen
decoder.onMalformedInput(CodingErrorAction.REPLACE);
decoder.onUnmappableCharacter(CodingErrorAction.REPLACE);
// Weisen Sie radikal unterschiedliche Eingabe- und Ausgabepuffergrößen zu
// zu Testzwecken
ByteBuffer bb = ByteBuffer.allocateDirect(16 * 1024);
CharBuffer cb = CharBuffer.allocate(57);
// Puffer beginnt leer; zeigt an, dass eine Eingabe erforderlich ist
CoderResult result = CoderResult.UNDERFLOW;
boolean eof = false;
while (!eof) {
// Eingabepufferunterlauf; Decoder möchte mehr Eingabe
if (result == CoderResult.UNDERFLOW) {
// Decoder verbraucht alle Eingaben, bereitet das Nachfüllen vor
bb.clear();
// Füllen Sie den Eingabepuffer; achten Sie auf EOF
eof = (source.read(bb) == -1);
// Bereiten Sie den Puffer für das Lesen durch den Decoder vor
bb.flip();
}
// Eingabebytes in Ausgabezeichen dekodieren; EOF-Flag übergeben
result = decoder.decode(bb, cb, eof);
// Wenn der Ausgabepuffer voll ist, Ausgabe entleeren
if (result == CoderResult.OVERFLOW) {
drainCharBuf(cb,writer);
}
}
// Alle verbleibenden Zustände aus dem Decoder löschen, dabei vorsichtig sein
// um Ausgabepufferüberläufe zu erkennen
while (decoder.flush(cb) == CoderResult.OVERFLOW) {
drainCharBuf(cb,writer);
}
// Alle im Ausgabepuffer verbleibenden Zeichen entfernen
drainCharBuf(cb,writer);
// Den Kanal schließen; alle gepufferten Daten an stdout senden
source.close();
Writer.flush();
}
/**
* Hilfsmethode zum Entleeren des Zeichenpuffers und zum Schreiben seines Inhalts in den angegebenen
* Writer-Objekt. Bei der Rückgabe ist der Puffer leer und kann wieder aufgefüllt werden.
*
* @param cb
* Ein CharBuffer, der zu schreibende Zeichen enthält.
* @param Autor
* Ein Writer-Objekt zum Verbrauchen der Zeichen in cb.
*/
static void drainCharBuf(CharBuffer cb, Writer write) löst eine IOException { aus
cb.flip(); // Puffer zum Entleeren vorbereiten
// Dies schreibt die im CharBuffer enthaltenen Zeichen, aber
// ändert den Zustand des Puffers nicht wirklich.
// Wenn der Zeichenpuffer durch Aufrufe von get( ) geleert wurde,
// Möglicherweise ist hier eine Schleife erforderlich.
if (cb.hasRemaining()) {
write.write(cb.toString());
}
cb.clear(); // Puffer zum erneuten Füllen vorbereiten
}
}
Bevor Sie die API durchsuchen, ist es wichtig zu erklären, wie die Charset-SPI funktioniert. Das Paket java.nio.charset.spi enthält nur eine Extraktionsklasse, CharsetProvider. Konkrete Implementierungen dieser Klasse stellen Informationen zu den von ihnen bereitgestellten Charset-Objekten bereit. Um einen benutzerdefinierten Zeichensatz zu definieren, müssen Sie zunächst spezifische Implementierungen von Charset, CharsetEncoder und CharsetDecoder aus dem Paket java.nio.charset erstellen. Anschließend erstellen Sie eine benutzerdefinierte Unterklasse von CharsetProvider, die diese Klassen der JVM bereitstellt.
Erstellen Sie einen benutzerdefinierten Zeichensatz:
Das Mindeste, was Sie tun müssen, ist, eine Unterklasse von java.nio.charset.Charset zu erstellen, konkrete Implementierungen der drei Extraktionsmethoden und einen Konstruktor bereitzustellen. Die Charset-Klasse verfügt über keinen standardmäßigen, parameterlosen Konstruktor. Das bedeutet, dass Ihre benutzerdefinierte Zeichensatzklasse über einen Konstruktor verfügen muss, auch wenn diese keine Parameter akzeptiert. Dies liegt daran, dass Sie den Konstruktor von Charset zum Zeitpunkt der Instanziierung aufrufen müssen (indem Sie super() am Anfang Ihres Konstruktors aufrufen) und ihn so mit dem Namen und Alias Ihrer Zeichensatzspezifikation versorgen. Auf diese Weise können die Methoden in der Charset-Klasse die namensbezogenen Dinge für Sie erledigen, das ist also eine gute Sache.
Ebenso müssen Sie konkrete Implementierungen von CharsetEncoder und CharsetDecoder bereitstellen. Denken Sie daran, dass ein Zeichensatz eine Sammlung codierter Zeichen und Codierungs-/Decodierungsschemata ist. Wie wir bereits gesehen haben, sind Kodierung und Dekodierung auf API-Ebene nahezu symmetrisch. Hier wird kurz erläutert, was zur Implementierung eines Encoders erforderlich ist. Das Gleiche gilt für den Aufbau eines Decoders.
Ähnlich wie Charset verfügt CharsetEncoder über keinen Standardkonstruktor, daher müssen Sie super() im konkreten Klassenkonstruktor aufrufen und die erforderlichen Parameter bereitstellen.
Um Ihre eigene CharsetEncoder-Implementierung bereitzustellen, müssen Sie mindestens die konkrete encodeLoop()-Methode bereitstellen. Für einfache Codierungsalgorithmen sollte die Standardimplementierung anderer Methoden einwandfrei funktionieren. Beachten Sie, dass encodeLoop() ähnliche Parameter wie encode() akzeptiert, mit Ausnahme des booleschen Flags. Die Methode encode() stellt die eigentliche Codierung für encodeLoop() dar, die nur auf die vom CharBuffer-Parameter verbrauchten Zeichen achten und die codierten Bytes an den bereitgestellten ByteBuffer ausgeben muss.
Nachdem wir nun gesehen haben, wie benutzerdefinierte Zeichensätze einschließlich der zugehörigen Encoder und Decoder implementiert werden, schauen wir uns an, wie wir sie mit der JVM verbinden, damit wir Code mit ihnen ausführen können.
Geben Sie Ihren benutzerdefinierten Zeichensatz an:
Um Ihre eigene Charset-Implementierung für die JVM-Laufzeitumgebung bereitzustellen, müssen Sie konkrete Unterklassen der CharsetProvider-Klasse in java.nio.charsets.-spi erstellen, jede mit einem parameterlosen Konstruktor. Der parameterlose Konstruktor ist wichtig, da Ihre CharsetProvider-Klasse durch Lesen des vollständig qualifizierten Namens der Konfigurationsdatei gefunden wird. Diese Klassennamenzeichenfolge wird dann in Class.newInstance() importiert, um Ihren Anbieter zu instanziieren, was nur über den parameterlosen Konstruktor funktioniert.
Die von der JVM gelesene Konfigurationsdatei findet den Zeichensatzanbieter mit dem Namen java.nio.charset.spi.CharsetProvider. Es befindet sich im Quellverzeichnis (META-INF/services) im JVM-Klassenpfad. Jedes JavaArchive (JAR) verfügt über ein META-INF-Verzeichnis, das Informationen zu den Klassen und Ressourcen in diesem JAR enthält. Ein Verzeichnis mit dem Namen META-INF kann auch an der Spitze der regulären Verzeichnisse im JVM-Klassenpfad platziert werden.
Die CharsetProvider-API ist nahezu nutzlos. Die eigentliche Arbeit zur Bereitstellung eines benutzerdefinierten Zeichensatzes erfolgt in der Erstellung benutzerdefinierter Charset-, CharsetEncoder- und CharsetDecoder-Klassen. Der CharsetProvider ist lediglich ein Vermittler zwischen Ihrem Zeichensatz und der Laufzeitumgebung.
Beispiel 6-3 demonstriert die Implementierung eines benutzerdefinierten Charsets und CharsetProviders, einschließlich Beispielcode, der die Zeichensatzverwendung, Codierung und Decodierung sowie den Charset-SPI veranschaulicht. Beispiel 6-3 implementiert einen benutzerdefinierten Zeichensatz.
Beispiel 6-3. Angepasster Rot13-Zeichensatz
Paket com.ronsoft.books.nio.charset;
import java.nio.CharBuffer;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CoderResult;
java.util.Map importieren;
import java.util.Iterator;
java.io.Writer importieren;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.OutputStreamWriter;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.FileReader;
/**
* Eine Charset-Implementierung, die eine Rot13-Kodierung durchführt, ist a
* einfacher Textverschleierungsalgorithmus, der alphabetische Zeichen um 13 verschiebt
* sodass aus „a“ „n“, aus „o“ „b“ usw. wird. Dieser Algorithmus wurde populär
* von den Usenet-Diskussionsforen vor vielen Jahren, um unanständige Worte zu maskieren, zu verstecken
* Antworten auf Fragen usw. Der Rot13-Algorithmus ist symmetrisch
* Wenn Sie den Text mit Rot13 verschlüsseln, erhalten Sie das Original
* entschlüsselter Text.
*
* Das Anwenden dieser Charset-Codierung auf einen Ausgabestream führt zu allem, was Sie tun
* Schreiben Sie in diesen Stream, um Rot13 zu verschlüsseln, während er ausgeschrieben wird
* Durch die Verknüpfung mit einem Eingabestream werden die gelesenen Daten beim Lesen Rot13-entschlüsselt.
*
* @Autor Ron Hitchens ([email protected])
*/
öffentliche Klasse Rot13Charset erweitert Charset {
// der Name der Basis-Zeichensatzkodierung, an die wir delegieren
private static final String BASE_CHARSET_NAME = "UTF-8";
// Handle für den tatsächlichen Zeichensatz, den wir für die Transkodierung zwischen verwenden werden
// Zeichen und Bytes. Dadurch können wir Rot13 anwenden
// Algorithmus zu Multibyte-Zeichensatzkodierungen
// ASCII-Alpha-Zeichen werden unabhängig von der Basiskodierung gedreht.
Charset baseCharset;
/**
* Konstruktor für den Rot13-Zeichensatz. Rufen Sie den Superklassen-Konstruktor auf
* Geben Sie den/die Namen weiter, unter denen wir bekannt sind. Speichern Sie dann einen Verweis darauf
* Zeichensatz delegieren.
*/
protected Rot13Charset(String canonical, String[] aliases) {
super(kanonisch, Aliase);
// Speichern Sie den Basiszeichensatz, an den wir delegieren
baseCharset = Charset.forName(BASE_CHARSET_NAME);
}
//------------------------------------------------ ----------
/**
* Wird von Benutzern dieses Zeichensatzes aufgerufen, um einen Encoder dieser Implementierung zu erhalten
* instanziiert eine Instanz einer privaten Klasse (unten definiert) und übergibt sie
* ein Encoder aus dem Basis-Charset.
*/
public CharsetEncoder newEncoder() {
return new Rot13Encoder(this, baseCharset.newEncoder());
}
/**
* Wird von Benutzern dieses Zeichensatzes aufgerufen, um einen Decoder zu erhalten
* instanziiert eine Instanz einer privaten Klasse (unten definiert) und übergibt sie
* ein Decoder aus dem Basis-Charset.
*/
public CharsetDecoder newDecoder() {
return new Rot13Decoder(this, baseCharset.newDecoder());
}
/**
* Diese Methode muss durch konkrete Zeichensätze implementiert werden. Wir sagen immer nein,
* was sicher ist.
*/
öffentlicher boolescher Wert enthält(Charset cs) {
return (falsch);
}
/**
* Gemeinsame Routine zum Drehen aller ASCII-Alpha-Zeichen im angegebenen
* CharBuffer um 13. Beachten Sie, dass dieser Code explizit für obere und obere Werte vergleicht
* ASCII-Zeichen in Kleinbuchstaben verwenden, anstatt die Methoden zu verwenden
* Character.isLowerCase und Character.isUpperCase
* Das Schema „Rotieren um 13“ funktioniert nur für die alphabetischen Zeichen von
* Der ASCII-Zeichensatz und diese Methoden können für Nicht-ASCII-Unicode „true“ zurückgeben
* Zeichen.
*/
private void rot13(CharBuffer cb) {
for (int pos = cb.position(); pos < cb.limit(); pos++) {
char c = cb.get(pos);
char a = '/u0000';
// Handelt es sich um Kleinbuchstaben?
if ((c >= 'a') && (c <= 'z')) {
a = 'a';
}
// Handelt es sich um Großbuchstaben?
if ((c >= 'A') && (c <= 'Z')) {
a = 'A';
}
// Wenn beides der Fall ist, würfle um 13
if (a != '/u0000') {
c = (char) ((((c - a) + 13) % 26) + a);
cb.put(pos, c);
}
}
}
//------------------------------------------------ --------
/**
* Die Encoder-Implementierung für die Rot13 Chars et
* Passende Decoder-Klasse unten, sollte auch die „impl“-Methoden überschreiben,
* wie implOnMalformedInput( ) und führen Sie Passthrough-Aufrufe an die durch
* baseEncoder-Objekt. Das bleibt dem Hacker als Übung überlassen.
*/
Die private Klasse Rot13Encoder erweitert CharsetEncoder {
privater CharsetEncoder baseEncoder;
/**
* Konstruktor, rufen Sie den Superklassenkonstruktor mit dem Charset-Objekt auf
* und die Codierungsgrößen vom Delegate-Encoder.
*/
Rot13Encoder(Charset cs, CharsetEncoder baseEncoder) {
super(cs, baseEncoder.averageBytesPerChar(), baseEncoder
.maxBytesPerChar());
this.baseEncoder = baseEncoder;
}
/**
* Implementierung der Kodierungsschleife Zuerst wenden wir Rot13 an
* Verschlüsselungsalgorithmus für den CharBuffer, dann den Encoder zurücksetzen
* den Basiszeichensatz und rufen Sie die Methode encode( ) auf, um den eigentlichen Vorgang auszuführen
*-Kodierung. Dies funktioniert möglicherweise nicht ordnungsgemäß für nicht-lateinische Zeichensätze
* Der übergebene CharBuffer kann schreibgeschützt sein oder vom Aufrufer wiederverwendet werden
* Für andere Zwecke duplizieren wir es und wenden die Rot13-Codierung auf das an
* kopieren. Wir möchten die Position des Eingabepuffers verschieben
* spiegelt die verbrauchten Zeichen wider.
*/
protected CoderResult encodeLoop(CharBuffer cb, ByteBuffer bb) {
CharBuffer tmpcb = CharBuffer.allocate(cb.remaining());
while (cb.hasRemaining()) {
tmpcb.put(cb.get());
}
tmpcb.rewind();
rot13(tmpcb);
baseEncoder.reset();
CoderResult cr = baseEncoder.encode(tmpcb, bb, true);
// Wenn ein Fehler oder ein Ausgabeüberlauf auftritt, müssen wir eine Anpassung vornehmen
// die Position des Eingabepuffers, der mit was übereinstimmt
// wurde wirklich vom temporären Puffer verbraucht
// Unterlauf (alle Eingaben verbraucht), dies ist ein No-Op.
cb.position(cb.position() - tmpcb.remaining());
return(cr);
}
}
//------------------------------------------------ --------
/**
* Die Decoder-Implementierung für den Rot13-Zeichensatz.
*/
Die private Klasse Rot13Decoder erweitert CharsetDecoder {
privater CharsetDecoder baseDecoder;
/**
* Konstruktor, rufen Sie den Superklassenkonstruktor mit dem Charset-Objekt auf
* und übergeben Sie die Zeichen/Byte-Werte vom Delegaten-Decoder.
*/
Rot13Decoder(Charset cs, CharsetDecoder baseDecoder) {
super(cs, baseDecoder.averageCharsPerByte(), baseDecoder
.maxCharsPerByte());
this.baseDecoder = baseDecoder;
}
/**
* Implementierung der Decodierungsschleife Zuerst setzen wir den Decoder zurück
* den Basiszeichensatz, dann rufen Sie ihn auf, um die Bytes in Zeichen zu dekodieren,
* Speichern des Ergebniscodes. Der CharBuffer wird dann mit dem entschlüsselt
* Rot13-Algorithmus und der Ergebniscode werden zurückgegeben. Dies funktioniert möglicherweise nicht
* ordnungsgemäß für nicht-lateinische Zeichensätze.
*/
protected CoderResult decodeLoop(ByteBuffer bb, CharBuffer cb) {
baseDecoder.reset();
CoderResult result = baseDecoder.decode(bb, cb, true);
rot13(cb);
Rückkehr (Ergebnis);
}
}
// -------------------------------------------- --------
/**
* Unit -Test für den ROT13 -Charsatz.
* Datei, wenn in der Befehlszeile oder stdin benannt, wenn keine Argumente bereitgestellt werden, und
* Schreiben Sie den Inhalt über die x -ROT13 -CHARSET -Codierung an stdout
* "Verschlüsselung" durch den ROT13 -Algorithmus ist symmetrisch
* In einer Klartextdatei wie z. B. Java-Quellcode z. B. Ausgabe a
* Durchgeschlagene Version.
* Original-Dokument der Klartext.
*/
public static void main (String [] argv) löst Ausnahme {aus {
BufferedReader in;
if (argv. Länge> 0) {
// Öffnen Sie die benannte Datei
in = neuer BufferedReader (neuer Fileeader (argv [0]));
} anders {
// Wickeln Sie einen BufferedReader um Stdin
in = neuer BufferedReader (neuer InputStreamReader (System.in));
}
// Erstellen Sie einen Printstream, der die ROT13 -Codierung verwendet
PrintStream out = new printStream (System.out, false, "x -ROT13");
Zeichenfolge s = null;
// Alle Eingaben lesen und in die Ausgabe schreiben.
// Wenn die Daten durch den Printstream gehen,
// Es wird rot13-codiert.
while (s = in.readline ())! = null) {
out.println (s);
}
out.flush();
}
}
Beispiel 6-4.
Paket com.ronsoft.books.nio.charset;
importieren java.nio.charset.charset;
importieren java.nio.charset.spi.charsetProvider;
import java.util.HashSet;
Import Java.util.iterator;
/**
* Eine CharsetProvider -Klasse, die die Charsets zur Verfügung stellt
* Ronsoft
* Kein registrierter Iana Charset, daher beginnt der Name mit "X-", um den Namen zu vermeiden
* Zusammenstöße mit offiziellen Charsets.
*
* Um diesen CharsetProvider zu aktivieren, müssen eine Datei zu der hinzugefügt werden
* Klassenpfad der JVM -Laufzeit am folgenden Ort:
* Meta-inf/dienste/java.nio.charets.spi.charsetProvider
*
* Diese Datei muss eine Linie mit dem voll qualifizierten Namen dieser Klasse enthalten
* Eine Zeile für sich selbst: com.ronsoft.books.nio.charset.ronsoftCharsetProvider Java
* Nio 216
*
* Siehe die Javadoc -Seite für java.nio.charets.spi.charsetProvider für Full
* Details.
*
* @Author Ron Hitchens ([email protected])
*/
Public Class RonsoftCharSetProvider erweitert CharsetProvider {
// Der Name des Charset, den wir angeben
private statische endgültige Zeichenfolge charset_name = "x-root13";
// ein Handle zum Charset -Objekt
private charset rot13 = null;
/**
* Konstruktor, instanziieren Sie ein Charset -Objekt und speichern Sie die Referenz.
*/
public ronsoftCharsetProvider () {
this.ROT13 = neuer Rot13CharSet (charset_name, neuer String [0]);
}
/**
* Aufgrund von Charset statischen Methoden, um einen bestimmten namens Charset zu finden, wenn
* Es ist der Name dieses Charset (wir haben keine Aliase) und geben dann die zurück
* ROT13 CHARSET, sonst geben Sie NULL zurück.
*/
public charSet charSetForname (String charSetName) {
if (charSetName.equalSignoreCase (charset_name)) {
return (rot13);
}
return (null);
}
/**
* Geben Sie einen Iterator über die von uns bereitgestellten Zeichenobjekte zurück.
*
* @return ein Iteratorobjekt, das Verweise auf alle Charset enthält
* Objekte, die von dieser Klasse bereitgestellt werden.
*/
public iterator <charset> charsets () {
Hashset <Schars> set = new Hashset <Scharset> (1);
set.add (rot13);
return (set.iterator ());
}
}
Durch das Hinzufügen von X -ROT13 in die Character -Set -Liste in Beispiel 6-1 wird diese zusätzliche Ausgabe erzeugt:
Charset: X-ROT13
Eingabe: żmaana?
Codiert:
0: C2 (ż)
1: bf (ż)
2: 5a (z)
3: 6e (n)
4: C3 (ă)
5: B1 (±)
6: 6e (n)
7:61 (a)
8: 6e (n)
9: 3f (?)
Charset (Zeichensatzklasse)
Ein Charakter -Set -Codierungsschema, das die Codierung, die verwendet wird, um Zeichensequenzen darzustellen, die sich von dem Zeichensatz als Sequenz von Bytes unterscheiden.
CharSetEnCoder (Zeichensatz für Codierungsklasse)
Codierung von Engine wandelt Zeichensequenzen in Byte -Sequenzen um. Die Byte -Sequenz kann dann dekodiert werden, um die Quellzeichensequenz zu rekonstruieren.
CharSetDecoder (Charset Decoder -Klasse)
Die Dekodierungsmotor wandelt die codierte Byte -Sequenz in eine Zeichensequenz um.
CharsetProvider SPI (Charset -Anbieter SPI)
Suchen Sie und machen Sie die Charset -Implementierung über den Serveranbietermechanismus zur Verwendung in der Laufzeitumgebung verfügbar.