Der Java-Datentyp string wird im JNI als Datentyp jstring bereitgestellt. Dieser Typ kann in C nicht direkt verwendet werden, insbesondere hat er nichts mit dem C-Datentyp char * gemein. Um den String in eine Form zu konvertieren, die in C bearbeitbar ist, müssen die entsprechenden JNI-Schnittstellen zur Konvertierung benutzt werden (siehe JNI-Dokumentation).
Der Java-Datentyp char wird an der JNI-Schnittstelle als Datentyp jchar bereitgestellt. Dieser ist mit dem C-Datentyp unsigned short kompatibel und repräsentiert ein Zeichen in Unicode-Darstellung. Unicode ist für die ersten 256 Zeichen identisch mit der ASCII-Codierung nach ISO8859-1. Für Unicode-Zeichen außerhalb dieses Bereiches gibt es in C/C++ im BS2000 keinerlei Support, die Verarbeitung solcher Zeichen muss also vom Anwender selbst vorgenommen werden.
Eine besondere Rolle spielt die UTF-8-Darstellung von Unicode, die von Java im JNI teilweise verwendet wird. In der UTF-8-Darstellung werden Unicode-Zeichen in ein, zwei oder drei Bytes verschlüsselt. Dabei sind die Unicode-Zeichen mit der Codierung 1 bis 127 mit diesem Wert in einem Byte dargestellt, was wiederum exakt der ASCII-Codierung dieser Zeichen entspricht.
Außerdem werden von Java UTF-8 Bytefolgen immer mit einem NULL-Byte abgeschlossen, was die Verarbeitung als C-Strings ermöglicht. Um das zu erreichen, wird das Unicode-NULL-Zeichen in zwei Byte verschlüsselt, um eine Verwechslung mit dem String-Ende in C zu vermeiden, denn im Gegensatz zu C dürfen in Java Strings durchaus auch NULL-Zeichen enthalten.
Für die Verarbeitung von UTF-8-Bytefolgen in C gelten also folgende einfache Regeln:
Das NULL-Byte kennzeichnet das Ende der Bytefolge und ist zwingend notwendig.
Bytes, für die die Funktion isascii_ascii() den Wert „
true
“ liefert (1-127), sind auch tatsächlich ASCII-Zeichen nach ISO8859-1Für alle anderen Bytes gilt, dass sie Bestandteil von Mehr-Byte-Folgen zur Darstellung von Unicode-Zeichen außerhalb des Bereiches 1-127 sind. Diese müssen vom Anwender selbst interpretiert werden.
Da fast alle diese Konvertierungsfunktionen Zeichenfolgen zumindest in einer zu ASCII aufwärtskompatiblen Form darstellen, spielt die Code-Umsetzung von ASCII nach EBCDIC und umgekehrt im BS2000 eine besondere Rolle. Dies gilt natürlich nicht nur für Strings, sondern auch z.B. für Byte-Arrays oder Characters (jchar).
Wenn hier von ASCII die Rede ist, dann ist immer der Zeichensatz ISO8859-1 (ISO-Latin1) oder sein 7-Bit Ableger (ISO 646) gemeint. Bei EBCDIC ist der Zeichensatz DF04-1 (Internationale Referenzversion) mit vertauschtem 0x15 und 0x25 bzw. sein 7-Bit-Ableger DF03-1 gemeint.
Neben expliziten Konvertierungsmöglichkeiten stehen für die Unterstützung von ASCII-Strings entsprechende Compiler- und Laufzeitsystem-Erweiterungen bereit, die es erlauben, direkt mit ASCII-Strings und Zeichen in C zu arbeiten.
Explizite Konvertierung
Die Konvertierungsfunktionen des JNI (siehe „Java TM Native Interface “ [13]) arbeiten im BS2000 exakt wie spezifiziert. Sie liefern bzw. erwarten immer Unicode oder UTF-8.
Für die explizite Konvertierung zwischen ASCII (8859-1) und EBCDIC (DF04-1) stehen einige Funktionen im CRTE zur Verfügung. Diese sind deklariert in einem Headerfile <ascii_ebcdic.h>, das Bestandteil der CRTE-Distribution ist. Diese Konvertierungsfunktionen sind im Handbuch „CRTE“ [3] beschrieben.
Beispiel
Das folgende Beispiel zeigt die Verwendung in einer native Methode, die den Wert einer Umgebungsvariablen ermittelt und von diesem das Präfix JAVA_ entfernt. Auf Java-Seite sei die Methode deklariert als:
public native String get_jenviron(String name);
Das zugehörige C-Programm könnte folgendermaßen aussehen:
#include <jni.h> #include ".....h" // von javah generierter Header #include <stdlib.h> #include <ascii_ebcdic.h> JNIEXPORT jstring JNICALL Java_..._get_jenviron(JNIEnv *env, jobject jthis, jstring name) { const char *utf_name; char *ebcdic_name, *ebcdic_value, *utf_value; jstring value; utf_name = (env*)->GetStringUTFChars(env,name,NULL), ebcdic_name = _a2e_dup(utf_name); (*env)->ReleaseStringUTFChars(env,name,utf_name); ebcdic_value = getenv(ebcdic_name); free(ebcdic_name); if (ebcdic_value == NULL) return NULL; if (strncmp(ebcdic_value,"JAVA_",5) == 0) utf_value = _e2a_dup(ebcdic_value+5); else utf_value = _e2a_dup(ebcdic_value); value = (*env)->NewStringUTF(env,utf_value); free(utf_value); return value; }
Der obige Beispielcode enthält keinerlei Fehlerbehandlung. Es wird implizit davon ausgegangen, dass in allen Strings nur Zeichen des 7-Bit-ASCII-Zeichensatzes vorkommen. Außerdem ist dieser Code natürlich stark BS2000-spezifisch.
ASCII-Strings im C-Code
Der C/C+-Compiler ab der Version 3.0B hat die Möglichkeit, alternativ zum normalen EBC-DIC-Encoding für String- und Character-Literale auch ein entsprechendes ASCII-Encoding zu generieren. Diese Einstellung muss für eine ganze Compilations-Einheit (Source-Datei) gelten und wird über die Compiler-Optionen -Kliteral_encoding_ascii bzw. -Kliteral_encoding_ascii_full gesteuert. Der Unterschied der beiden Optionen bezieht sich auf die Behandlung von Oktal- und Hexadezimal-Sequenzen in solchen Literalen. Die Umwandlung von solchen Literalteilen unterbleibt bei -Kliteral_encoding_ascii.
ASCII-Strings im C-Laufzeitsystem
Das C-Laufzeitsystem hat neben den o.g. Konvertierungsroutinen noch weitere Unterstützung für die Nutzung von ASCII-Strings und -Zeichen. Alle wesentlichen XPG4-Funktionen, die mit Strings oder Zeichen arbeiten oder solche liefern, stehen in einer Variante für ASCII-Codierung bereit. Beim Setzen einer der in Abschnitt „ASCII-Strings im C-Code" beschriebenen Compiler-Optionen für ASCII-Nutzung werden normalerweise automatisch die entsprechenden Library-Funktionen benutzt, ohne dass der Anwender dafür etwas tun muss. Für den Mischbetrieb können Sie das Verhalten auch verändern (siehe Handbuch „CRTE“ [3]).
Ist gleichzeitig die Compiler-Option -Kieee_floats gesetzt, so werden die kombinierten AS-CII/IEEE-Varianten benutzt (z.B. bei printf).
Ab dem C-Compiler V3.1A und CRTE V2.4C werden die Argumente des Vektors argv[] bei der Compilierung des Main-Programms mit einer der in Abschnitt „ASCII-Strings im C-Code" beschriebenen Compiler-Optionen als ASCII-Strings übergeben. Die globalen Variablen des C-Laufzeitsystems tzname und die Strings von environ werden als ASCII-Strings gespeichert. Die explizite Konvertierung von argv[] entfällt damit.
Falls explizit auf die Strings der globalen Variablen tzname oder environ zugegriffen wird, so ist zu beachten, dass diese Strings seit JENV V1.4B als ASCII-Strings abgelegt sind (vorher EBCDIC-Strings). Vom expliziten Zugriff auf die Variable environ wird aber durch den Technical Standard „The Single UNIX Specification“ abgeraten (siehe „X/Open System Interface (XSI) Specification“ [16]). Der implizite Zugriff über getenv() und putenv() funktioniert wie bisher kompatibel zu Vorversionen.
Beispiel
Das obige C-Programm könnte mit diesen Möglichkeiten jetzt folgendermaßen aussehen:
#include <jni.h> #include ".....h" // von javah generierter Header #include <stdlib.h> JNIEXPORT jstring JNICALL Java_..._get_jenviron(JNIEnv *env, jobject jthis, jstring name) { const char *utf_name; char *utf_value; utf_name = (*env)->GetStringUTFChars(env,name,NULL); utf_value = getenv(utf_name); (*env)->ReleaseStringUTFChars(env,name,utf_name); if (utf_value == NULL) return NULL; if (strncmp(utf_value,"JAVA_",5) == 0) return (*env)->NewStringUTF(env,utf_value+5); else return (*env)->NewStringUTF(env,utf_value); }
Dies entspricht vollständig einer Implementierung, die auch auf Unix-Systemen zum Einsatz kommen könnte. Daher ist diese Form sicher am ehesten für portierten Code zu empfehlen.