1 5. Darstellung und Verarbeitung von Textzeichen 5.1. Einführung Computer werden nicht nur zum Rechnen mit Zahlen oder zur Verarbeitung aussagenlogischer Werte eingesetzt. Man kann mit ihnen auch Texte speichern und verarbeiten. Bei der Programmierung unterscheidet man Textzeichen und Zeichenketten: • Ein (Text-)Zeichen (engl. Character) ist ein einzelnes Zeichen, wie beispielsweise ein lateinischer Buchstabe (A, ..., Z, a, ..., z), eine Ziffer (0, ..., 9), ein Sonderzeichen (!, ?, #, ’, +, -, ....), oder ein Schriftzeichen aus einem anderen Alphabet oder einer anderen Sprache (Russisch mit dem kyrillischen Alphabet, Griechisch, Arabisch, Hebräisch, Chinesisch, Japanisch, ...). • Eine Zeichenkette (engl. String) ist eine Folge von Zeichen, wie beispielsweise die Wörter, Sätze, Abschnitte und Programmbeispiele dieses Texts. Eine Zeichenkette kann auch nur ein einziges Zeichen enthalten oder gar keines, also leer sein. 5.2. Standards zur Zeichencodierung Computer speichern und verarbeiten Daten in binärer Form, also durch Nullen und Einsen. Ein Zeichen kann dabei dargestellt („codiert“) werden, indem man ihm eine natürliche Zahl (also eine nichtnegative ganze Zahl) zuordnet und dann deren Binärdarstellung im Speicher ablegt. Benötigt wird also ein Standard, der jedem möglichen Zeichen eine eindeutige natürliche Zahl zuordnet, so dass man von der gespeicherten Zahl eindeutig auf das Zeichen schließen kann. Java benutzt zur Zeichencodierung den Unicode-Standard, der eine Erweiterung des ASCIIStandards ist. 5.2.1. ASCII ASCII („American Standard Code for Information Interchange“) ist ein „7-Bit-Code“. Er stellt also ein Zeichen durch eine Kombination aus sieben Bits dar, d.h. eine natürliche Zahl zwischen 0 und 127. ASCII umfasst • die Ziffern 0 - 9 • die lateinischen Groß- und Kleinbuchstaben: A - Z, a - z • Sonderzeichen wie (, ), !, ., :, *, #, + • Steuerzeichen wie Zeilenvorschub, Seitenvorschub oder Tabulator © Prof. C. Vogt, 2017 Verwendung nur mit Quellenangabe und außerhalb der TH Köln nur mit Genehmigung des Autors. 2 Die Codierung der 128 ASCII-Zeichen wird durch die ASCII-Tabelle definiert: 0 16 32 48 64 80 96 112 +0 NUL DLE Leerz. 0 @ P ` p +1 SOH DC1 ! 1 A Q a q +2 STX DC2 " 2 B R b r +3 ETX DC3 # 3 C S c s +4 EOT DC4 $ 4 D T d t +5 ENQ NAK % 5 E U e u +6 ACK SYN & 6 F V f v +7 BEL ETB ' 7 G W g w +8 BS CAN ( 8 H X h x +9 HT EM ) 9 I Y i y +10 LF SUB * : J Z j z +11 VT ESC + ; K [ k { +12 FF FS , < L \ l | +13 CR QS - = M ] m } +14 SO RS . > N ^ n ~ +15 SI US / ? O _ o DEL • Der ASCII-Code eines Zeichens ergibt sich als Summe der Beschriftungen seiner Zeile und Spalte. • Beispiele: Der ASCII-Code des Zeichens 'A' ist 65 (= 64 + 1). Der ASCII-Code des Zeichens 'a' ist 97 (= 96 + 1). Der ASCII-Code des Zeichens '+' ist 43 (= 32 + 11). Der ASCII-Code des Leerzeichens ' ' ist 32 (= 32 + 0). Der ASCII-Code des Zeilenvorschubs (Linefeed) 'LF' ist 10 (= 0 + 10). • Gleichartige Zeichen stehen (bis auf wenige Ausnahmen) in zusammenhängenden Bereichen der Tabelle, werden also durch Zahlen eines Intervalls codiert: • Großbuchstaben im Intervall [65,90] • Kleinbuchstaben im Intervall [97,122] • Ziffern im Intervall [48,57] • Steuerzeichen im Intervall [0,31] und bei 127 [Die meisten dieser Steuerzeichen haben heute kaum noch Bedeutung. Ausnahmen sind © Prof. C. Vogt, 2017 Verwendung nur mit Quellenangabe und außerhalb der TH Köln nur mit Genehmigung des Autors. 3 BS = Backspace = Rückwärtsschritt, LF = Line Feed = Zeilenvorschub, FF = Form Feed = Seitenvorschub, CR = Carriage Return = Ende der Eingabe und DEL = Delete = Löschen eines Zeichens] Aufgabe 1: Was ist der ASCII-Code des Fragezeichens und was ist der Code der schließenden eckigen Klammer? Welches Zeichen codiert die Zahl 95 und welches die Zahl 120? ASCII ist zwar ein 7-Bit-Code, aus praktischen Gründen werden ASCII-codierte Zeichen aber in einem Byte, also acht Bits gespeichert. Das achte Bit kann für eine Erweiterung des Zeichensatzes genutzt werden, also zur Codierung weiterer Zeichen durch die Codes 128, ..., 255. Ein Beispiel für eine solche Erweiterung ist Latin-1, das u.a. Umlaute (ä, ö, ...) und Vokale mit Akzent (á, à, ...) umfasst. ASCII und seine 8-Bit-Erweiterungen sind durch die ISO (International Organization for Standardization) standardisiert, so z.B. Latin-1 als ISO-Standard 8859-1. 5.2.2. Unicode ASCII berücksichtigt nur eine sehr kleine Teilmenge der weltweit benutzten Zeichen. Um dem abzuhelfen, wurde Unicode eingeführt. Unicode ist in seiner ursprünglichen Form ein 16-BitCode; später wurde er auf 32 Bits erweitert. Unicode-Zeichen werden also binär in Zwei- oder Vier-Byte-Variablen gespeichert. Der Zahlenbereich 0-255 von Unicode entspricht ASCII Latin-1. Höhere Zahlen codieren die Zeichensätze weiterer Schriftsprachen, z.B. das kyrillische Alphabet ab 1024 oder die chinesischen Schriftzeichen ab 19968. Unicode ist unter der ISO-Nummer 10646 standardisiert. Näheres (auch zu ASCII und seinen Erweiterungen) findet man unter http://www.unicode.org. Aufgabe 2: Gehen Sie zu http://www.unicode.org/charts/ um zu sehen, welche Alphabete und Sprachen in Unicode berücksichtigt sind. 5.3. Zeichen in Java 5.3.1. Zeichenkonstanten Java basiert auf dem 16-Bit-Unicode. Einzelzeichen werden also durch zwei Bytes codiert, wie durch die Unicode-Tabellen festgelegt. Relevant für den deutsch- und englischsprachigen Raum ist dabei der Zahlenbereich von 0 bis 255, der ASCII Latin-1 entspricht. In Java kann man eine Zeichenkonstante (also einen Wert aus dem Unicode-Zeichensatz) auf verschiedene Arten schreiben: • Das Zeichen in '' (also in einfachen Hochkommata) • Beispiele: 'A' 'a' 'α' '#' • Die „Unicode-Escape-Sequenz“ des Zeichens. Sie hat die Form '\uxxxx', wobei xxxx für die vier Hexadezimalziffern steht, die den Unicode des Zeichens angeben. • Beispiele: '\u0041' für A, '\u0061' für a, '\u03B1' für α, '\u0023' für # © Prof. C. Vogt, 2017 Verwendung nur mit Quellenangabe und außerhalb der TH Köln nur mit Genehmigung des Autors. 4 • Randbemerkung: Man kann solche Sequenzen an jeder beliebigen Stelle des Java-Quelltexts einsetzen, also z.B. in Zeichenketten (siehe nächster Abschnitt) oder in Variablennamen. • Für spezielle Zeichen: Eine „Java-Escape-Sequenz“ der Form '\x', wobei x für ein Zeichen gemäß der folgenden Tabelle steht. Bezeichnung Zeichen BS (Back Space, Schritt nach links) '\b' HT (horizontaler Tabulator) '\t' LF (Line Feed, Zeilenende) '\n' FF (Form Feed, vertikaler Tabulator) '\f' CR (Carriage Return, Wagenrücklauf) '\r' " (doppeltes Hochkomma) '\"' ' (einfaches Hochkomma) '\'' \ (Back Slash, umgekehrter Schrägstrich) '\\' 5.3.2. Zeichenvariablen Der Java-Typ für einzelne Zeichen heißt char. Sein Wertebereich ist die Menge der Zahlencodes gemäß 16-Bit-Unicode. char-Konstanten in Java-Programmen werden dargestellt wie im vorangehenden Abschnitt beschrieben. • Beispiel: Deklaration und Initialisierung zweier char-Variablen char a='A', b='\u0023'; Aufgabe 3: Deklarieren Sie eine char-Variable und weisen Sie ihr das Zeichen α zu. Berücksichtigen Sie dabei, dass dieses Zeichen auf Ihrer Tastatur nicht vorhanden ist, Sie es also nicht direkt eintippen können. char-Werte werden durch System.out.println() ausgegeben. Ein char-Wert wird durch System.in.read() eingelesen. Da read() einen Ganzzahlwert liefert, muss dieser Wert explizit nach char umgewandelt werden. • Beispiel: Ausgabe zweier char-Werte und Einlesen eines char-Werts char a = ..., b = ...; System.out.println("a = "+a+" b = "+b); char c = (char) System.in.read(); Die Java-Klasse Character definiert eine Reihe nützlicher Methoden zum Umgang mit char-Werten. Der folgende Programmausschnitt zeigt einige von ihnen: • Beispiel: Einige Methoden der Klasse Character char c = ...; System.out.println("Ist es ein Buchstabe? " +Character.isLetter(c)); © Prof. C. Vogt, 2017 Verwendung nur mit Quellenangabe und außerhalb der TH Köln nur mit Genehmigung des Autors. 5 System.out.println("Ist es ein Grossbuchstabe? " +Character.isUpperCase(c)); System.out.println("Ist es ein Kleinbuchstabe? " +Character.isLowerCase(c)); System.out.println("Ist es eine Ziffer? " +Character.isDigit(c)); System.out.println("Ist es eine Ziffer oder ein Buchstabe? " +Character.isLetterOrDigit(c)); System.out.println("Entsprechender Grossbuchstabe: " +Character.toUpperCase(c)); System.out.println("Entsprechender Kleinbuchstabe: " +Character.toLowerCase(c)); Da char-Werte intern als ganze Zahlen dargestellt werden, kann man mit ihnen rechnen. Beispielsweise kann man für ein Zeichen seinen Vorgänger und seinen Nachfolger in der UnicodeTabelle ermitteln – im Fall von Buchstaben also den vorherigen und den nächsten Buchstaben im Alphabet. • Beispiel: Ermittlung des Vorgängers und des Nachfolgers eines char-Werts char c = ...; char vorgaenger = (char)(c-1); char nachfolger = (char)(c+1); Aufgabe 4: Welchen Wert hat der Ausdruck (char)('m'-5)? Welchen Wert hat der Ausdruck (char)('9'+4)? Entsprechend sind sämtliche Wertvergleiche, die in Kap. 4 eingeführt wurden, zulässig: ==, !=, <, <=, >, >=. Aufgabe 5: Laden Sie das Programmbeispiel http://www.nt.th-koeln.de/vogt/dv/java/Daten/ Chars.java herunter, führen Sie es aus und vollziehen Sie seine Operationen im Quellcode nach. Verändern und erweitern Sie es nach Ihren eigenen Ideen. 5.4. Zeichenketten / Strings in Java 5.4.1. Konstanten für Zeichenketten Konstante Zeichenketten („Stringkonstanten“) werden in Java durch Zeichenfolgen dargestellt, die in "" eingeschlossen sind (also in doppelten Hochkommata = dem Zeichen, das sich auf einer deutschen Tastatur oberhalb des Zeichens 2 befindet). Hierbei sind beliebige Unicode-Zeichen zugelassen, auch in ihrer Darstellung als Escape-Sequenzen. • Beispiele: "Hallo" "Erste Zeile\nZweite Zeile" "alpha: \u03b1" • Achtung: Man muss gut unterscheiden zwischen Einzelzeichen und Zeichenketten. Beispielsweise ist 'c' eine char-Konstante, "c" ist dagegen eine Stringkonstante der Länge 1. Es handelt sich dabei um unterschiedliche, nicht direkt kompatible Werte und Typen! © Prof. C. Vogt, 2017 Verwendung nur mit Quellenangabe und außerhalb der TH Köln nur mit Genehmigung des Autors. 6 5.4.2. Variablen: Klasse String Stringkonstanten und -variablen können in Java mit dem Typ String realisiert werden. Für das grundlegende Verständnis dieses Typs sind die folgenden Begriffe und Zusammenhänge wichtig: • String-Objekte speichern String-Konstanten. • String-Variablen speichern Referenzen auf String-Objekte. Das folgende Beispiel erklärt und illustriert diese Aussagen: • Die Deklaration String s1; deklariert eine String-Variable. • Die Anweisung new String("Hallo") erzeugt ein neues String-Objekt. s1 s1 Hallo • Die Zuweisung s1 s1 = new String("Hallo"); Hallo weist der String-Variablen s1 eine „Referenz“ auf das neu erzeugte Objekt zu. Man sagt dann, dass s1 das Objekt „referenziert“, also darauf „verweist“, und man kann von s1 aus über die Referenz auf den Inhalt des Objekts (hier: die String-Konstante "Hallo") zugreifen. Man muss also klar unterscheiden zwischen String-Objekten, die String-Konstanten speichern, und String-Variablen, in denen Referenzen auf String-Objekte stehen. Diese Unterscheidung ist wichtig bei String-Operationen in Java – insbesondere bei Zuweisungen: Bei Zuweisungen werden Referenzen, aber keine Objekte kopiert! • Beispiel: Zuweisung zwischen String-Variablen s1 s1 = new String("Hallo"); Hallo s2 = s1; s2 Bei der Zuweisung wird der Inhalt der Variablen s1 in die Variable s2 kopiert. Da der s1-Inhalt eine Referenz ist, referenziert s2 anschließend dasselbe Objekt wie s1. Das Objekt selbst wird nicht kopiert, so dass auch nach der Zuweisung nur ein String-Objekt vorhanden ist. Aufgabe 6: Deklarieren Sie eine String-Variable, erzeugen Sie ein String-Objekt, das Ihren Namen als Zeichenkette enthält, und lassen Sie die Variable das Objekt referenzieren. Strings können mit dem +-Operator aneinandergehängt („konkateniert“) werden. Diese Operation haben Sie bereits in println()-Aufrufen gesehen, wo der Ausgabetext durch die Konkatenation von Zeichenketten aufgebaut wurde. Bei der Anwendung des +-Operators ist relevant, dass String-Objekte nicht „editierbar“ sind, der Inhalt eines String-Objekts also nicht geändert werden kann. Bei einer Konkatenation entsteht also ein neues String-Objekt; der Inhalt des vorhandenen Objekts bleibt unverändert. © Prof. C. Vogt, 2017 Verwendung nur mit Quellenangabe und außerhalb der TH Köln nur mit Genehmigung des Autors. 7 • Beispiel: Zuweisung zwischen String-Variablen Hallo Welt und anschließende Konkatenation s1 s1 = new String("Hallo"); Hallo s2 = s1; s2 s1 = s1 + new String("Welt"); Nach der Zuweisung s2=s1 referenzieren beide Variablen dasselbe Objekt mit Inhalt "Hallo" (siehe voriges Beispiel). Durch die anschließende Konkatenation entsteht ein neues Objekt mit Inhalt "Hallo Welt", das (wegen der Zuweisung s1=...) von s1 referenziert wird. Das bisherige "Hallo"-Objekt bleibt unverändert und wird nach wie vor von s2 referenziert. Aufgabe 7: Betrachten Sie die folgende Anweisungsfolge String a, b, c; a = new String("Anton"); b = new String("Berta"); c = new String("Cäsar"); a = b; b = b + new String(" Müller"); Zeichnen Sie eine Skizze, die die String-Variablen und -Objekte sowie die Referenzen in ihnen zeigt. Bei der Ausführung von Java-Programmen kommt es oft vor, dass Objekte von keiner Variablen mehr referenziert werden, also nicht mehr zugreifbar sind. Solche Objekte werden durch das Java-Laufzeitsystem (genauer: den „Java Garbage Collector“) automatisch gelöscht und ihr Speicherplatz wieder freigegeben; der Programmierer muss sich hierum nicht kümmern, Die Java-Klasse String definiert eine Reihe nützlicher Methoden zum Umgang mit Zeichenketten. Die folgende Auflistung zeigt einige von ihnen: • s1.length(): Länge des Strings = Anzahl der Zeichen • s1.equals(s2): Vergleich zweier Strings (Details dazu siehe unten) • s1.compareTo(s2): ebenfalls Vergleich zweier Strings (Details dazu siehe unten) • s1.startsWith("Hal"): Prüfung, ob der String ein bestimmtes Anfangsstück hat • s1.endsWith("elt"): Prüfung, ob der String ein bestimmtes Endstück hat • s1.contains("llo"): Prüfung, ob der String einen bestimmten Teilstring enthält • s1.indexOf("llo"): Position, an der ein bestimmter Teilstring beginnt (wobei die Positionszählung in einem String immer bei 0 beginnt) • s1.charAt(2): Zeichen, das an einer bestimmten Position des Strings steht • s1.substring(2): Teilstring, der an einer bestimmten Position des Strings beginnt Bei der Arbeit mit String-Variablen ist es wichtig, zwischen Vergleichen mit == und Vergleichen mit equals() zu unterscheiden: == vergleicht Referenzen in String-Variablen, equals() vergleicht String-Konstanten in String-Objekten. © Prof. C. Vogt, 2017 Verwendung nur mit Quellenangabe und außerhalb der TH Köln nur mit Genehmigung des Autors. 8 • Beispiel: Vergleich von Strings. s1 Hallo String s1 = new String("Hallo"); String s2 = new String("Hallo"); s2 Hallo Hier referenzieren s1 und s2 zwei String-Objekte, die zwar denselben Inhalt haben, aber zwei verschiedene Objekte sind. s1==s2 vergleicht die Inhalte der String-Variablen (also die Referenzen) und liefert false, da s1 und s2 verschiedene Objekte referenzieren. s1.equals(s2) vergleicht die Inhalte der String-Objekte und liefert true, da die Zeichenketten in den Objekten gleich sind. equals() prüft lediglich, ob zwei Strings gleich oder ungleich sind, und liefert entsprechend einen boolean-Wert. compareTo() stellt dagegen fest, welcher der beiden Strings gemäß der „lexikographischen Reihenfolge“ (nach der z.B. Wörter in einem Wörterbuch sortiert sind) als erster steht. s1.compareTo(s2) liefert eine 0, wenn beide Strings gleich sind, eine negative Zahl, wenn s2 lexikographisch nach s1 steht, und eine positive Zahl sonst. Aufgabe 8: Welchen Wert liefert der folgende equals()-Aufruf? String a = "Anton"; ... a.equals("anton"); Aufgabe 9: Laden Sie das Programmbeispiel http://www.nt.th-koeln.de/vogt/dv/java/Daten/ Strings.java herunter, führen Sie es aus und vollziehen Sie seine Operationen im Quellcode nach. Verändern und erweitern Sie es nach Ihren eigenen Ideen. Aufgabe 10: Gehen Sie zur Online-Dokumentation der Klasse String (http://docs.oracle.com/javase/9/docs/api/ > im linken unteren Fenster "String" suchen und anklicken) und betrachten Sie die Methoden, die diese Klasse anbietet. 5.4.3. Variablen: Klasse StringBuffer Neben der Klasse String gibt es in Java die Klasse StringBuffer. StringBuffer-Objekte sind, im Gegensatz zu String-Objekten, „editierbar“; man kann also die Zeichenkette im Objekt ändern. Die Klasse StringBuffer definiert einige Methoden, mit denen man auf StringBufferObjekten arbeiten kann. Zwei Beispiele sind die Methoden replace() und reverse(): Mit replace() kann man Teile einer Zeichenkette durch andere Zeichen ersetzen, mit reverse() kann man die gespeicherte Zeichenkette „umdrehen“. Aufgabe 11: Laden Sie das Programmbeispiel http://www.nt.th-koeln.de/vogt/dv/java/Daten/Stringbuf.java herunter und vollziehen Sie seine Operationen nach. Verändern und erweitern Sie es nach Ihren eigenen Ideen. Aufgabe 12: Gehen Sie zur Online-Dokumentation der Klasse StringBuffer (http://docs.oracle.com/javase/9/docs/api/ > im linken unteren Fenster "StringBuffer" anklicken) und betrachten Sie die Methoden, die diese Klasse anbietet. Aufgabe 13: Laden Sie das Programmbeispiel http://www.nt.th-koeln.de/vogt/dv/java/EinAus/StringEingabe.java herunter. Vollziehen Sie nach, wie das Programm eine Zeichen- © Prof. C. Vogt, 2017 Verwendung nur mit Quellenangabe und außerhalb der TH Köln nur mit Genehmigung des Autors. 9 kette von der Tastatur in ein String-Objekt einliest und wie die Zeichenkette daraus einem StringBuffer-Objekt zugewiesen wird. 5.5. Lernergebnisse dieses Kapitels Nach diesem Kapitel muss man mindestens wissen, • wie die ASCII-Tabelle Zeichen durch Zahlen codiert, • was der Unterschied zwischen ASCII und Unicode ist, • warum und wie man mit Zeichen rechnen kann und • was der Unterschied zwischen Zeichen und Zeichenketten ist. In Java muss man jetzt mindestens • Variablen des Typs char deklarieren und initialisieren können, • Zeichenkonstanten darstellen können, • zu einem Zeichen das nächste und das vorangehende ermitteln können • Zeichenketten in String- und StringBuffer-Objekten speichern und verarbeiten können und • zwischen String-Variablen und String-Objekten unterscheiden können, insbesondere bei Zuweisungen und Vergleichen. 5.6. Lösung der Aufgaben Aufgabe 1: 63, 93, _ (Unterstrich), x Aufgabe 2: Aufgabe 3: char alpha = '\u03B1'; Aufgabe 4: 'h', '=' (nicht 13, denn hier wird nicht mit der Zahl 9, sondern mit dem Unicode/ ASCII-Code des Zeichens '9', also 57 gerechnet) Aufgabe 5: Aufgabe 6: String ich = new String("Charlie Brown"); Aufgabe 7: a Anton b Berta Berta Müller c Cäsar Aufgabe 8: false (denn es wird zwischen Groß- und Kleinbuchstaben unterschieden) © Prof. C. Vogt, 2017 Verwendung nur mit Quellenangabe und außerhalb der TH Köln nur mit Genehmigung des Autors.