4.6 Zeichenkodierungen
 
Zeichen sind in Java immer in Unicode kodiert, und ein String ist eine Folge von Zeichen. Wollen wir diese Zeichenkette etwa in eine Datei schreiben, so kann es bei Zeichen, die nicht im ASCII-Code enthalten sind, zu Problemen kommen. Die Unicode-Strings können daher umkodiert werden.
4.6.1 Kodierungen für unterschiedliche Codepages
 
Die String-Klasse konvertiert mit der Methode getBytes(String encoding) den String in eine andere Zeichen-Kodierung, etwa vom internen Unicode in den EBCDIC-Zeichensatz der IBM-Mainframes. Sun repräsentiert jede Kodierung (engl. encodings) durch eine Zeichenfolge, die unter http://java.sun.com/j2se/1.5/docs/guide/intl/encoding.doc.html aufgeführt ist. Die Kodierung, die den alten IBM-Zeichensatz bezeichnet, heißt »Cp850«. Die Windows-NT-Konsole nutzt zum Beispiel dieses Format. Für den alten EBCDIC-Zeichensatz ist die Codepage »Cp037«.
4.6.2 Andere Klassen zur Konvertierung und das Paket java.nio.charset
 
Neben der Klasse String mit getBytes() unterstützen auch andere Klassen die Umkodierung. Dazu zählt die Klasse OutputStreamWriter, die als Writer die Unicode-Zeichen mit einer gewählten Kodierung in einen binären Datenstrom schreibt. Der InputStreamReader übernimmt den anderen Weg.
Beispiel Zum korrekten Darstellen der Umlaute auf der DOS-Konsole wird ein OutputStreamWriter mit der Codepage 850 angewiesen.
Listing 4.8
GetBytesConverter.java
import java.io.*;
public class GetBytesConverter
{
public static void main( String[] args )
{
try
{
System.out.println( "Ich kann Ä Ü Ö und ß" );
PrintWriter out = new PrintWriter(
new OutputStreamWriter(System.out, "Cp850") );
out.println( "Ich kann Ä Ü Ö und ß" );
out.flush();
}
catch ( UnsupportedEncodingException e ) {
System.err.println(e); }
}
}
|
Sollen ganze Dateien umkodiert werden, lässt sich das Dienstprogramm native2ascii nutzen.
Seit Java 1.4 übernimmt das Paket java.nio.charset im Hintergrund die Kodierungen. Die statische Funktion Charset.availableCharsets() liefert eine Map<String,Charset> – mit etwa 150 Einträgen – und somit Namen und assoziierte Klassen alle angemeldeten Kodierer. Die konkreten Kodierungsklassen lassen sich aber auch per Charset.forName() erfragen.
for ( String charsetName : Charset.availableCharsets().keySet() )
{
System.out.println( charsetName );
Charset charset = Charset.forName( charsetName );
}
Mit dem konkreten Charset-Objekt lässt sich auf zwei Wegen weiter verfahren:
|
Direkt mit den Methoden encode() und decode() konvertieren oder |
|
über die newDecoder() einen CharsetDecoder beziehungsweise newEncoder() einen CharsetEncoder erfragen und darüber arbeiten. |
4.6.3 Base64-Kodierung
 
Für die Übertragung von Binärdaten im Internet hat sich die Base64-Kodierung durchgesetzt. Die im RFC 1521 beschriebene Methode übersetzt ein Byte, welches ja aus 8 Bit besteht, in 6-Bit-Buchstaben, was reines ASCII ist. Da nun aber 2 Bit fehlen, werden die in einem anderen Zeichen kodiert. Allerdings gibt es jetzt nicht 2 Bits in einem extra Zeichen, sondern der größte gemeinsame Teiler von 6 und 8 wird gesucht, das ist 24. Also können 3 Ausgangszeichen in 4 Base64-kodierte Zeichen umgesetzt werden. Binärdaten werden also rund 33 % länger.
Für die Kodierung gibt es in der Java-Bibliothek von Sun zwar Unterstützung, aber nicht ganz legal: Die Kodierer liegen im Paket sun.misc. Wem das nicht ganz geheuer ist, der kann unter http://jakarta.apache.org/commons/codec/ die Commons-Bibliothek beziehen.
Das nachfolgende Beispiel erzeugt zuerst ein Bytefeld der Größe 200 und belegt es mit Zufallszahlen. Sun-Klassen kodieren das Bytefeld in einen String, der auf dem Bildschirm ausgegeben wird. Nachdem der String wieder zurückkodiert wird, werden die Bytefelder verglichen und liefern natürlich true. Da die Java-Bibliothek leider über keine direkte Funktion zum Vergleich von Bytefeldern verfügt, müssen wir über einen Trick gehen: Wir bauen erst ByteBuffer-Objekte auf und vergleichen dann über die equals()-Funktion.
Listing 4.9
Base64Demo.java
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Random;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
public class Base64Demo
{
public static void main( String[] args ) throws IOException
{
byte bytes1[] = new byte[200];
new Random().nextBytes( bytes1 );
// buf in String
String s = new BASE64Encoder().encode( bytes1 );
System.out.println( s );
// Zum Beispiel:
// KWB6vmM/nk3CrP8S2keDrty8pCZGnOueEuR0zOYqeGNJrF8CSpo79t9JcVBKy30laAXwVmnJ+/mV
// MdplnXHhKe+6VqGKXW76ksiJKbDx5poIkP2LJ9d8JPg7tKTQPUY4ROQt0S8SekbE5xhfR2rYhM7K
// k9Qs37tvH+MuuCnlEvrj4H6jqIrSl/R9nYVM5pfegZe10M32phXCg0axl1sPPF7kxsxLKy5Yslcx
//j3/V6+kbjhHqfn/rg+s9A/HPN/NauYd3m87CaDA=
// String in byte[]
byte bytes2[] = new BASE64Decoder().decodeBuffer( s );
boolean isEqual = ByteBuffer.wrap(bytes1).equals( ByteBuffer.wrap(bytes2) );
System.out.println( isEqual ); // true
}
}
|