1. Zeichenkettenverarbeitung

Im Folgenden wollen wir uns mit Zeichen und Zeichenfolgen beschäftigen und insbesondere mit den Datentypen char, Character, String und StringBuilder.

Methoden können primitive Werte annehmen und auch zurückgeben und auch Objektreferenzen annehmen und zurückgeben. Strings sind Objekte, die man dynamisch aufbauen kann, etwa mit dem Plus-Operator. Das wollen wir in diesem Kapitel üben.

Lernziele

  • API von String und StringBuilder sicher nutzen können

  • Erkennen, wann String und StringBuilder besser geeignet ist

Verwendete Datentypen in diesem Kapitel:

1.1. String-Objekte

String-Objekte sind in Java allgegenwärtig, sie sind so omnipräsent, dass viele Entwickler sie als eingebaute Datentypen betrachten. Dabei sind Strings nur Objekte, genießen in der Sprache aber Vorzüge. Als Objekte haben sie Methoden, und um diese Eigenschaften handelt der nächste Abschnitt.

1.1.1. Quiz: Ist String ein Schlüsselwort? ⭐

Java hat eingebaute Datentypen, darunter int, double, boolean. Ist String ebenfalls ein primitiver eingebauter Datentyp? Ist String ein Schlüsselwort in Java?

1.1.2. HTML-Elemente aufbauen mit einfacher Konkatenation ⭐

In HTML werden für Auszeichnungen Tags eingesetzt, ein Beispiel ist <b><i>Bold und Italic</i></b>.

Aufgabe:

  1. Schreibe eine neue Methode htmlElement(String, String), die ein String mit einem Start- und End-Tag eingerahmt. Als Beispiel: htmlElement("b", "Bold ist fett.") liefert "<b>Bold ist fett</b>".

  2. Schreibe eine neue Methode bold(String) und italic(String), die im Hintergrund mit htmlElement(…​) arbeiten und ein <b> respektive <i> erzeugen.

1.1.3. Strings füllen ⭐

Captain CiaoCiao liebt die Freiheit und Abstand ist ihm sehr wichtig. Auch bei Texten findet er, könnten die Buchstaben ruhig ein wenig mehr Abstand haben.

Aufgabe:

  • Schreibe eine Methode mix(String, String), die einen String aufspreizt, und Füllzeichen zwischen jedes Zeichen setzt.

Beispiele:

  • mix("Der Rum ist alle!", " ")"D e r R u m i s t a l l e !"

  • mix("Blimey", "👻")"B👻l👻i👻m👻e👻y"

  • mix("👻", "👻")"👻"

  • mix("", "👻")""

1.1.4. Sicher übermittelt durch Verdoppelung der Zeichen? ⭐

Bamboo Blobfish nutzt einen Typendrucktelegraf um Captain CiaoCiao wichtige Nachrichten mitzuteilen. Da es auf jedes Zeichen ankommt, sendet er alle Zeichen zur Sicherheit zweimal hintereinander.

Aufgabe:

  • Schreibe eine Methode boolean isEveryCharcterTwice(String), die prüft, ob jedes Zeichen im String zweimal hintereinander vorkommt.

    • Kommt nicht jedes Symbol zweimal hintereinander vor ist die Nachricht falsch und die Rückgabe der Methode 0.

    • Kommt jedes Zeichen zweimal vor, ist die Antwort eine beliebige positive Zahl.

    • Wenn nicht zwei gleiche Zeichen hintereinander kommen gibt die Methode die Position mit der ersten falsche Stelle zurück, allerdings negiert.

Beispiele:

  • isEveryCharacterTwice("eehhrrwwüürrddiiggeerr$$ccaappttaaiinn")1

  • isEveryCharacterTwice("ccapptttaaiinn")-3

  • isEveryCharacterTwice("222")0

1.1.5. Ist das japanisch? ⭐

Man sagt Japanern nach, dass sie statt ›l‹ ein ›r‹ sprechen. Das wollen wir ausprobieren, wie sich das ›anhört‹.

Aufgabe:

  1. Lege eine neue Klasse TalkLikeAJapanese an.

  2. Setze in die Klasse eine neue Klassenmethode void talkJapanese(String s), die einen übergebenen String auf dem Bildschirm bringt, aber den Buchstaben "r"/"R" als "l"/"L" ausgibt. Es geht nicht darum einen String zurückzuliefern.

Beispiel: talkJapanese("Riesenrad") ergibt auf dem Bildschirm die Ausgabe Liesenlad.

  • Teste die Methode mit einer main-Methoden.

  • Schreibe nicht nur eine Variante, sondern versuche mindestens zwei Varianten zu programmieren.

1.1.6. Trotzige Antworten geben ⭐

Tony der Trotzige ist für den Schwarzmarkt von Captain CiaoCiao zuständig, doch er wird geschnappt und von der Polizei verhört. Um die Polizisten zu nerven, wiederholt er alles, was sie sagen und setzt ein ›Keine Ahnung!‹ hinten dran. Fragt der Polizist: ›Wo ist das Schwarzmarktlager?‹, sagt Tony: ›Wo ist das Schwarzmarktlager? Keine Ahnung!‹

Aufgabe:

  1. Lege eine neue Klasse an und Frage von der Kommandozeile eine Eingabe ab.

  2. Auf dem Bildschirm gib aus, was von der Eingabe kommt, hänge aber ein " Keine Ahnung!" hinten dran.

  3. Wenn von der Polizei keine Frage gestellt wird — die Eingabe endet nicht mit ? — hält Tony der Trotzige ganz seinen Mund.

  4. Kommt von der Eingabe selbst "Keine Ahnung?", und das unabhängig von der Groß-/Kleinschreibung, erwidert Tony trotzig "Aye!".

1.1.7. Quiz: Stringvergleiche? ⭐

Wie lassen sich Strings in Java vergleichen? Nenne mindestens zwei Möglichkeiten?

1.1.8. Quiz: Ist equals(…​) symmetrisch? ⭐

Unter der Annahme, dass s ein String ist, gibt es einen Unterschied zwischen s.equals("tutego") und "tutego".equals(s)?

1.1.9. Zeichenfolgen auf Palindrom-Eigenschaft testen ⭐

Ein Palindrom ist ein Wort, das sich von vorne wie von hinten gleich liest, etwa Otto oder auch 121.

Dass es überhaupt solche Worte und sogar Sätze gibt amüsiert Captain CiaoCiao, kann er damit doch die Gesellschaft unterhalten. Allerdings legt man ihn immer wieder Zeichenfolgen vor, die keine Palindrome sind. Daher müssen alle Wörter vorher getestet werden.

Aufgabe:

  • Schreibe ein Java-Programm, welches untersucht, ob eine Zeichenkette ein Palindrome ist.

    • Lege eine neue Klasse PalindromeTester an.

    • Implementiere eine Methode boolean isPalindrome(String s).

    • Erweitere das Programm um eine Klassenmethode isPalindromeIgnoringCase(String s), sodass der Test unabhängig von der Groß-/Kleinschreibung wird.

    • Jetzt sollen auch alle Zeichen ignoriert werden, die keine Buchstaben oder Ziffern sind. Bei der Feststellung hilft Character.isLetterOrDigit(char). Damit kann man auch Sätze wie A man a plan a canal Panama oder Pepe in Tahiti hat nie Pep oder Sei fies – stets sei fies! prüfen. Nennen wir die Methode isPalindromeIgnoringNonLettersAndDigits(String).

1.1.10. Prüfen, ob Captain CiaoCiao in der Mitte steht ⭐

Captain CiaoCiao ist der Mittelpunkt der Welt, also erwartet er, dass er in allen Texten auch im Zentrum steht.

Aufgabe:

  • Schreibe eine Methode boolean isCiaoCiaoInCenter(String), die true liefert, wenn die Zeichenfolge "CiaoCiao" in der Mitte steht.

Beispiele:

  • isCaptainCiaoCiaoInMiddle("CiaoCiao")true

  • isCaptainCiaoCiaoInMiddle("!CiaoCiao!")true

  • isCaptainCiaoCiaoInMiddle("SupaCiaoCiaoCute")true

  • isCaptainCiaoCiaoInMiddle("x!_CiaoCiaoabc")true

  • isCaptainCiaoCiaoInMiddle("\t\tCiaoCiao ")true

  • isCaptainCiaoCiaoInMiddle("BambooCiaoCiaoBlop")false

  • isCaptainCiaoCiaoInMiddle("BabyTigerChristine")false

1.1.11. Kürzesten Namen im Array finden ⭐

Captain CiaoCiao nutzt nur den kürzesten Rufnamen einer Person.

Aufgabe:

  • Schreibe eine Methode String shortestName(String…​ names), die von allen vollständigen Namen den kürzesten Teilstring zurückgibt. Namen sind durch höchsten ein Leerzeichen getrennt.

  • Wenn es keine Namen gibt, ist die Antwort ein leerer String.

  • Kein String im Array darf null sein.

Beispiel:

  • shortestName("Albert Tross", "Blowfish", "Nick Olaus", "Jo Ker")"Jo"

1.1.12. Vorkommen zählen ⭐

Captain CiaoCiao hat in einer unbedachten Aktion den Entwickler Dev David ausgeschaltet. Der war gerade dabei eine Methode zu schreiben; die Javadoc ist fertig, aber die Implementierung fehlt.

/**
 * Counts how many times the substring appears in the larger string.
 *
 * A {@code null} or empty ("") String input returns {@code 0}.
 *
 * <pre>
 * StringUtils.countMatches(null, *)       = 0
 * StringUtils.countMatches("", *)         = 0
 * StringUtils.countMatches("abba", null)  = 0
 * StringUtils.countMatches("abba", "")    = 0
 * StringUtils.countMatches("abba", "a")   = 2
 * StringUtils.countMatches("abba", "ab")  = 1
 * StringUtils.countMatches("abba", "xxx") = 0
 * </pre>
 *
 * @param string  the String to check, may be null
 * @param other   the substring to count, may be null
 * @return the number of occurrences, 0 if either String is {@code null}
 */
public static int countMatches( String string, String other ) { return null; }

Aufgabe:

  • Implementiere die Methode.

1.1.13. Die größere Mannschaft ermitteln ⭐⭐

Captain CiaoCiao findet in den alten Logbüchern eine Aufstellung der Mannschaftsstärke der Piratenschiffe, sowie der gekaperten Schiffe.

|-|||
|-||
|||-|||
|||||-||

Jedes Crew-Mitglied ist durch einen Strich symbolisiert, ein Minuszeichen trennt die Mannschaftsgröße. Links steht die Anzahl Personen auf dem Piratenschiff, rechts die Anzahl vom überfallenen Schiff.

Aufgabe:

  • Die Striche sind für Captain CiaoCiao nicht gut zu lesen. Schreibe ein Programm, was die Kodierung deutlich macht:

    |-||| => Raided ship had a larger crew, difference 2
    |-|| => Raided ship had a larger crew, difference 1
    |||-||| => Ships were the same size
    |||||-|| => Pirate ship had a larger crew, difference 3

1.1.14. Diamanten bauen ⭐⭐

Captain CiaoCiao mag Diamanten, je größer desto besser.

Aufgabe:

  • Schreibe ein Programm, was folgende Ausgabe generiert:

       A
      ABA
     ABCBA
    ABCDCBA
     ABCBA
      ABA
       A

    Prinzipiell soll es möglich sein, den Umfang — die breiteste Stelle — anzugeben, was in dem in unserem Beispiel 7 ist. Wir wollen eine Breite ausschließen, die unmöglich mit aufsteigenden Großbuchstaben zu erreichen ist, also soll ABCDEFGHIJKLMNOPQRSTUVWXYZYXWVU…​BA das Breiteste sein.

1.1.15. Wörter unterstreichen ⭐⭐

Immer wieder muss Captain CiaoCiao seine Crew auf Regeln aufmerksam machen. Er schreibt dazu eine Nachricht und unterstreicht die wichtigen Wörter. Captain CiaoCiao hat im folgenden Text das Wort ›Geld‹ unterstrichen:

Du musst zuerst Geld machen! Wenn du das Geld hast, bekommst du Macht.
                ----                     ----

Aufgabe:

  1. Lege eine neue Klasse PrintUnderline an.

  2. Schreibe eine neue statische Methode printUnderline(String string, String search), die jede Zeichenkette underline in text wie im oberen Beispiel gezeigt unterstreicht. Bedenke, dass text mehrmals im String vorkommen kann, oder auch keinmal.

1.1.16. Vokale entfernen ⭐

Captain CiaoCiao diktiert seine Memoiren und es sprudelt aus ihm heraus. So schnell kann Kiko Kokopu gar nicht mitschreiben! Aber kann man nicht alle Vokale weglassen und es trotzdem später noch verstehen? Ein Sprachwissenschaftler sagte mal, dass ein Text auch nach dem Entfernen der Vokalbuchstaben noch verständlich bleibt. Als Vokalbuchstaben gelten im Deutschen: A, Ä, E, I, O, Ö, U, Ü, Y. Stimmt es, was die Wissenschaftler sagen?

Aufgabe:

  1. Lege eine neue Klasse RemoveVowel an.

  2. Schreibe eine Klassenmethode String removeVowels(String string), die aus einem übergebenen java.lang.String die Vokalbuchstaben entfernt. Es ist in Ordnung, wenn die Umlaute fehlen.

  3. Löse die Aufgabe mit mindestens zwei verschiedenen Varianten.

1.1.17. Auf ein gutes Passwort prüfen ⭐

Alle schmutzigen Geheimnisse verschlüsselt Captain CiaoCiao, aber zu oft war sein Passwort zu einfach, und wurde erraten. Captain CiaoCiao hat gelernt, dass ein sicheres Passwort für seine Geschäfte wichtig ist, doch kann er sich die Regeln nicht so richtig merken: Ein gutes Passwort hat eine gewisse Länge, enthält Sonderzeichen, usw.

Aufgabe:

  1. Lege eine neue Klasse PasswordTester an.

  2. Schreibe eine Methode isGoodPassword(String), die übliche Kriterien testet. Die Methode soll false zurückgeben, wenn das Passwort nicht gut ist, und true, wenn das Passwort einen guten Aufbau hat. Schlägt ein Test fehl, so soll über System.err eine Meldung erscheinen und keine weiteren Prüfungen stattfinden.

1.1.18. Quersumme berechnen ⭐⭐

Da Captain CiaoCiao oftmals Zahlungen anweist, und befürchtet, jemand könnte die Beträge verändern, greift er zu einem Trick: Neben dem Betrag übermittelt er in einem getrennten Kanal die Quersumme.

Die Quersumme einer Zahl bildet man durch die Addition jeder Ziffer der Zahl. Wenn die Zahl etwa 10938 lautet, so ist die Quersumme 1 + 0 + 9 + 3 + 8 = 21.

Aufgabe:

  1. Lege eine neue Kasse SumOfTheDigits an.

  2. Schreibe eine Klassenmethode int sumOfTheDigits(long value), die die Quersumme einer Zahl berechnet.

  3. Schreibe eine überladene Klassenmethode int sumOfTheDigits(String value) dazu, die die Ziffern in einem String annimmt.

Welche Methode ist leichter zu implementieren? Welche Methode sollte die andere als Unterprogramm aufrufen?

1.1.19. Entspalter ⭐⭐

Captain CiaoCiao scannt alte Filmzitate ein, doch die waren ursprünglich in Spalten. Nach der OCR-Texterkennung bleiben die Spalten erhalten.

Da das nicht gut zu lesen ist, sollen die zwei Spalten erkannt und in regulären Fließtext ohne Spalten übersetzt werden.

Aufgabe:

  • Schreibe eine Methode decolumnize(String), die nach der Spalte sucht und aus einem Text mit zwei Spalten einen Text mit einer Spalte liefert.

Beispiel:

I’m dishonest, and a to watch out for,
dishonest man you    because you can
can always trust to  never predict when
be dishonest.        they’re going to do
Honestly, it’s the   something incredibly
honest ones you want stupid.

I’m dishonest, and a
dishonest man you
can always trust to
be dishonest.
Honestly, it’s the
honest ones you want
to watch out for,
because you can
never predict when
they’re going to do
something incredibly
stupid.

Jede Spalte ist durch mindestens ein Leerzeichen getrennt. Bedenke, das die rechte und linke Seite "halbe" Leerzeilen haben kann!

1.1.20. Eine Wiese mit Lieblingsblumen zeichnen ⭐⭐

Captain CiaoCiao möchte sein Schiff verschönern und mit Blumen verzieren. Er findet von Joan G. Stark eine Grafik als Vorlage und überlegt, wie er den Malern und Anstreichern mitteilen kann, welche Muster er auf dem Poopdeck wünscht.

                _
              _(_)_                          wWWWw   _
  @@@@       (_)@(_)   vVVVv     _     @@@@  (___) _(_)_
 @@()@@ wWWWw  (_)\    (___)   _(_)_  @@()@@   Y  (_)@(_)
  @@@@  (___)     `|/    Y    (_)@(_)  @@@@   \|/   (_)\
   /      Y       \|    \|/    /(_)    \|      |/      |
\ |     \ |/       | / \ | /  \|/       |/    \|      \|/
\\|//   \\|//   \\\|//\\\|/// \|///  \\\|//  \\|//  \\\|//
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Aufgabe:

  1. Kopiere die Blumen in einen String. Tipp: Lege einen String wie String flower = ""; an, setze die Blumenreihe in die Zwischenablage und füge sie in der IDE zwischen die Anführungszeichen ein; IntelliJ und Eclipse werden dann die Sonderzeichen wie \ und \n selbständig kodieren.

  2. Es gibt 8 Sorten von Blumen und wir können sie durchnummerieren von 1 bis 8. Kodiert man die Reihenfolge als String sind die Blumen "12345678" angezeigt. Es soll nun möglich sein andere Reihenfolgen darzustellen und Blumen häufiger darzustellen, etwa durch die Kodierung "8383765432". Ist eine Kennung falsch, wird automatisch immer die erste Blume genommen.

1.1.21. Wiederholungen erkennen ⭐⭐⭐

Captain CiaoCiao blättert in einem Buch und findet Muster der Art 🌼🌻🌼🌻🌸🌼🌻🌼🌻🌸🌼🌻🌼🌻🌸. Wie beruhigend für ihn. Er möchte Stempel herstellen und fragt sich, welche Folge von Symbolen auf die Stempel müssten.

Aufgabe:

  • Schreibe eine Methode String repeatingStrings(String), die Wiederholungen erkennt und im Fall einer Wiederholung die sich wiederholende Zeichenfolge als Rückgabe liefert, sonst null.

Beispiele:

  • repeatingStrings("🌼🌼🌼") liefert "🌼".

  • repeatingStrings("🌼🌻🌼🌻🌼🌻") liefert "🌼🌻".

  • repeatingStrings("CiaoCiao") liefert "Ciao".

  • repeatingStrings("Captain CiaoCiaoCaptain CiaoCiao") liefert "Captain CiaoCiao".

  • repeatingStrings("🌕🌔🌓🌑") liefert null

  • repeatingStrings("CaptainCiaoCiaoCaptain") liefert null

  • repeatingStrings("🌼") liefert null

  • repeatingStrings("") liefert null

  • repeatingStrings(null) liefert null

Es reicht, wenn repeatingStrings(…​) die kürzeste sich wiederholende Zeichenfolge liefert.

1.1.22. Zeilengrenzen beschränken ⭐⭐

Captain CiaoCiao steigt in der Kommunikation auf Brieftauben um, und da kann das Papier nicht sehr groß sein. Jetzt muss er alle seine Texte in der Breite reduzieren.

Aufgabe:

  • Schreibe eine Klasse WordWrap mit einer statischen Methode String wrap(String string, int width), die eine Zeichenkette ohne Zeilenumbrüche in kleine Teilzeichenketten der maximalen Breite width zerlegt und mit \n getrennt wieder zurückgibt. Innerhalb von Wörtern soll nicht zwangsumgebrochen werden!

Ein Beispiel:

String s = "Ich habe nichts gesehen, ich habe nichts gehört, " +
           "ich war nie dort, und wenn doch, habe ich geschlafen.";
System.out.println( wrap( s, 30 ) );

Das liefert bei einer maximalen Zeilenlänge von 30 folgender Ausgabe:

Ich habe nichts gesehen, ich
habe nichts gehört, ich war
nie dort, und wenn doch, hab
ich geschlafen.

1.1.23. Quiz: Wie viele String-Objekte? ⭐

Wie viele String-Objekte gibt es im folgenden Programmcode?

String str1 = "tutego";
String str2 = new String( "tutego" );
String str3 = "tutego";
String str4 = new String( "tutego" );

1.1.24. Schokoladig umhüllt ⭐⭐

Captain CiaoCiao mag Obstspieße mit Schokolade überzogen. Sara bekommt die Aufgabe die Früchte zu glasieren, wobei sie unterschiedliche Schichten von dunkler und heller Schokolade bildet.

Bambi prüft, ob die Schichten korrekt sind. Sieht sie dhFhd weiß sie, dass die Frucht F erst eine Schicht helle, dann eine Schicht dunkle Schokolade bekommen hat. Bei dhhd fehlt die Frucht, und das entspricht nicht der Erwartung von Captain CiaoCiao. Bei ddhFh ist die Schicht kaputt, was auch nicht richtig ist. Und bei F fehlt die Schokolade komplett, was für eine Enttäuschung!

Aufgabe:

  • Schreibe eine rekursive Methode checkChocolate(String), die prüft, ob links und rechts der gleiche Typ Schokolade ist und sich in der Mitte die Frucht F befindet.

Optional: Erweitere das Programm, dass der String eine Fläche ist, etwa

String fruit =
  "ddddd\n" +
  "dhhhd\n" +
  "dhFhd\n" +
  "dhhhd\n" +
  "ddddd" ;

In einer Höhle findet Captain CiaoCiao einen Text, allerdings ist er nicht von links nach rechts, sondern von oben nach unten geschrieben.

s u
ey!
ao

Vertikal geschrieben ist das die Zeichenfolge sea you!-- und das ist viel einfacher zu lesen!

Aufgabe:

  • Schreibe eine Methode convertVerticalToHorizontalWriting(String), die einen String wieder in die horizontale Lage bringt und ausgibt. Das Argument ist ein String, in dem Zeilenvorschübe die Zeilen trennen, zum Beispiel:

    String s = "s u\n" +
               "ey!\n" +
               "ao ";

Zwei wichtige Annahmen sollen gelten:

  1. Zeilen werden nur mit \n getrennt,

  2. jede Zeile ist gleich lang; die letzte Zeile hat zum Beispiel ein Leerzeichen, damit alle Zeilen 3 Zeichen lang sind (Zeilenumbruch nicht eingerechnet),

  3. am Ende des Strings steht kein \n.

1.2. Dynamische Strings mit StringBuilder

Während String-Objekte immutable sind, lassen sich Objekte vom Typ java.lang.StringBuilder modifizieren. Gleiches gilt für StringBuffer, doch dieser Typ ist API-gleich und für die Übungen nicht relevant.

1.2.1. Der Papagei übt das Alphabet ⭐

Captain CiaoCiao lehrt seinem Papagei das Alphabet. Damit er sich die Mühe spart, greift er auf eine Software zurück, die ihm das Alphabet generiert, sodass es später in die Sprachausgabe geben kann.

Gegeben ist folgende Methode:

static String abcz() {
  String result;
  result = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
  return result;
}

Die Methode liefert eine Zeichenkette mit allen Zeichen im Alphabet von 'A' bis 'Z'. So kann Captain CiaoCiao die Zeichenkette in ein Eingabefeld auf https://ttsreader.com/de/ kopieren und vorlesen lassen. Allerdings müssen dann zwischen den Buchstaben Leerzeichen stehen, damit die Tonausgaben besser klingen.

Die Methode ist zwar performant in ihrer Aufgabe, aber nicht sonderlich flexibel, wenn es zum Beispiel darum geht nur gewisse Bereiche zu generieren, etwa von 'G' bis 'W' oder von '0' bis '9'. Denn der Papagei kann ABC schon sehr gut, aber von A bis Z wird es eng.

Aufgabe:

  • Ändere die Methode abcz(), sodass der String dynamisch über eine Schleife generiert wird.

  • Ergänze eine Methode String abcz(char start, char end), die ein String mit allen Symbolen generiert, die zwischen dem Anfangsbuchstaben start und dem Endbuchstaben end liegen; das Endezeichen ist inklusiv und gehört mit zum String.

  • Schreibe eine weitere Methode String abcz(char start, int length), die ab start dann length Zeichen liefert. Lässt sich eine der beiden Methoden aufeinander abbilden?

Hinweis: Überlege, wie mit fehlerhaften Parametern umzugehen ist.

1.2.2. Quiz: Leicht angehängt ⭐

Möchte man Zeichenfolgen dynamisch aufbauen, hat man in Java prinzipiell zwei Möglichkeiten:

  1. über die Klasse String und die String-Konkatenation mit +,

  2. über StringBuilder (StringBuffer wollen wir hier nicht explizit mit erwähnen — die beiden Klassen sind API-identisch).

In Code:

String s = "";
s += "Ay ";
s += "Captain";

StringBuilder sb = new StringBuilder();
sb.append( "Ay " ).append( "Captain" );
String t = sb.toString();

Inwiefern unterscheiden sich beide Lösungen? Wie viele Objekte werden generiert?

1.2.3. Gewicht verlieren durch Vertauschung ⭐ (NEU)

Captain CiaoCiao ist aufgefallen, dass er bei den Frachtgebühren prima betrügen kann. Die Verantwortlichen im Büro vergessen oft das genaue Gewicht, können sich aber die vorkommenden Ziffern perfekt merken und es fällt nicht auf, wenn maximal zwei Ziffern vertauscht werden. Captain CiaoCiao nutzt das zu seinem Vorteil, dass er die kleinste Ziffer in der Zahl — aber nicht 0, weil sich sonst die Länge der Zahl ändert, das fällt auf — nach vorne setzt, um ein kleineres Gewicht zu erzielen.

Aufgabe:

  • Schreibe ein Methode int cheatedWeight(int weight), die genau diese Transformation macht.

1.2.4. Don’t shoot the messenger ⭐

Captain CiaoCiao übermittelt eine geheime Nachricht und hat Angst, dass der Bote überfallen, und die Nachricht dadurch offenbart wird. Also schickt er mehrere Boten, die jeweils einen Teil der Nachricht übermitteln. Das Schema ist wie folgt:

  • Ein Text wird in Buchstaben zerlegt

  • Bote 1 bekommt den 1. Buchstaben

  • Bote 2 bekommt den 2. Buchstaben

  • Bote 1 bekommt den 3. Buchstaben

  • Bote 2 bekommt den 4. Buchstaben

  • usw.

Der Empfänger der Nachricht muss nun auf die beiden Boten warten, die ursprüngliche Reihenfolge kennen, und die Nachricht wieder zusammensetzen.

Aufgabe:

  • Schreibe eine Methode String joinSplitMessages(String…​), der man eine beliebige Anzahl von Strings der Boten übergeben kann. Die Rückgabe der Methode ist der zusammengesetzte String.

  • Falls Nachrichtenteile fehlen, soll das zu keinem Fehler führen.

Beispiele:

  • joinSplitMessages("Hoy", "ok")"Hooky"

  • joinSplitMessages("Hooky")"Hooky"

  • joinSplitMessages("Hk", "oy", "o")"Hooky"

  • joinSplitMessages( "H", "", "ooky" )"Hooky"

1.2.5. Wiederholte Leerzeichen komprimieren ⭐⭐

Bubbles hört für Captain CiaoCiao ein Gespräch ab und transkribiert es. Doch weil Bubbles immer so viele Erdnüsse schält, ist die Leertaste behindert und löst sich schlecht; so verdoppelt sich schnell ein Leerzeichen. Auf keinem Fall kann sie so den Text Captain CiaoCiao geben.

Aufgabe:

  • Schreibe eine statische Methode StringBuilder compressSpace(StringBuilder string), die mehr als zwei Leerzeichen im übergebenen string zu einem Leerzeichen zusammenschmelzen lässt.

  • Die Übergabe string soll mit return string; zurückgegeben werden, die Veränderung soll direkt am StringBuilder erfolgen.

Beispiel:

  • "Hallo ​ ​ ​ Welt ""Hallo Welt "

Optionale Erweiterungen:

  • Leerzeichen ganz am Anfang und ganz am Ende sollen komplett weggeschnitten werden.

  • Wenn ein Tabulator vorkommt, soll dieses wie ein Leerzeichen gewertet werden, sodass ein String wie "hallo \t welt" zu "hallo welt" wird.

1.2.6. Knacken und knistern einfügen und entfernen ⭐

Meldungen über Funk haben oft ein Knistern, das störtet Captain CiaoCiao.

Aufgabe. Schreibe zwei Methoden:

  • String crackle(String) soll in willkürlichen Abständen ›♬KNACK♪‹ einbauen und den Knisternstring zurückgeben

  • String decrackle(String) soll das Knistern wieder entfernen

1.2.7. Camel-Case-Strings zerlegen ⭐

Um bei der telegrafischen Übertragung von Text Volumen einzusparen, wendet Funker Frogfish einen Trick an: er schreibt das nächste Zeichen hinter dem Leerzeichen groß und löscht dann das Leerzeichen. Aus ciao ciao wird ciaoCiao. Ist das nächste Zeichen schon ein Großbuchstabe wird einfach nur das Leerzeichen entfernt, so wird aus Ciao Ciao dann CiaoCiao. Da die Großbuchstaben innerhalb der Serie von Kleinbuchstaben wie Höcker aussehen wird die Schreibweise Camel-Case getauft.

Captain CiaoCiao empfängt die Zeichenfolgen, aber leicht zu lesen ist so ein Text nicht.

Aufgabe:

  • Schreibe eine neue Methode String camelCaseSplitter(String), die alle Camel-Case-Segmente wieder trennt.

Beispiele:

  • camelCaseSplitter("List")List

  • camelCaseSplitter("CiaoCiao")Ciao Ciao

  • camelCaseSplitter("numberOfElements")number Of Elements

  • camelCaseSplitter("CiaoCiaoCAPTAIN")Ciao Ciao CAPTAIN

Falls Wörter komplett in Großbuchstaben sind, wie im letzten Fall, gilt nur der Wechsel zwischen Klein- und Großbuchstaben.

1.2.8. Caesar-Verschlüsselung implementieren ⭐⭐

Captain CiaoCiao hat von einer Verschlüsselung erfahren, die schon Gaius Julius Caesar verwendet haben soll. Da er den römischen Feldherrn bewundert, will auch er seine Texte so verschlüsseln.

Bei der sogenannten Caesar-Verschlüsselung verschiebt man jedes Zeichen um drei Positionen im Alphabet, das heißt, aus A wird D, aus B wird E und so weiter. Am Ende des Alphabets beginnen wir wieder von vorne und so ergibt XA, YB, ZC.

Aufgabe:

  1. Lege eine neue Klasse Caesar an.

  2. Implementiere eine Methode String caesar(String s, int rotation), die die Verschlüsselung vornimmt.rotation ist dabei die Verschiebung, die beliebig sein sollte, nicht nur 3 wie aus dem Eingangsbeispiel.

  3. Schreibe eine Methode String decaesar(String s, int rotation), die die Verschlüsselung wieder zurücknimmt.

  4. Die Caesar-Verschlüsselung fällt ist die Klasse der Verschiebechiffre. Sind sie Captain CiaoCiao zu empfehlen?

1.2.9. Verlusstfreie String-Kompression durch Lauflängenkodierung realisieren ⭐⭐⭐

Um das Datenvolumen zu reduzieren, werden Dateien oft komprimiert. Es gibt unterschiedliche Kompressionsalgorithmen, einige sind verlustbehaftet, wie das Entfernen von Vokalen, andere arbeiten ohne Verlust wie ZIP. Verlustbehaftete Kompression findet man z. B. bei Bildern, JPEG ist ein gutes Beispiel. In Abhängigkeit von dem Grad der Kompression verschlechtert sich die Bildqualität. Eine sehr hohe Kompression führt beim JPEG-Verfahren zu einem Bild mit starken Artefakten.

Eine einfache verlustfreie Kompression ist die Lauflängenkodierung. Das Prinzip dabei ist, Folge von gleichen Symbolen zusammenzufassen, dass nur noch die Anzahl und das Symbol geschrieben wird. Das Grafikformat GIF nutzt z. B. diese Form der Kompression. Daher sind Bilder mit vielen einfarbigen Zeilen auch kleiner als zum Beispiel Bilder, in denen jeder Bildpunkt eine andere Farbe hat.

Die nächste Aufgabe handelt um Lauflängenkodierung. Nehmen wir an, ein String besteht aus einer Folge von . (Punkt) und - (Minuszeichen), etwa

--....--------..-

Um die Länge von Zeichenketten zu verkürzen, können wir zunächst die das Symbol gefolgt von der Anzahl Symbole schreiben. Die Zeichenfolge mit 17 Zeichen könnte auf folgende Zeichenfolge mit 9 Zeichen verkürzt werden:

-2.4-8.2-

Aufgabe:

  1. Lege eine neue Klasse SimpleStringCompressor an.

  2. Schreibe eine statische Methode String compress(String), die Folgen von . und - nach dem beschriebenen Algorithmus kodiert: erst kommt das Zeichen, dann die Anzahl.

  3. Schreibe einen Dekodierer String decompress(String), der der komprimierten String wieder auspackt. Es soll uncompress(compress(input)) gleich input sein.

Erweiterungen:

  • Das Programm soll alle nicht-Ziffern verarbeiten können.

  • Verfeinere das Programm, dass die Zahl ausbleibt, wenn das Zeichen nur genau einmal vorkommt.