Umlaute einfügen in Caesar (Java)

Bei einer Unterrichtsaufgabe ergab sich für uns folgendes Problem:

Unser erstelltes Programm tut im Prinzip genau das, was es tun soll. Es verschlüsselt nach dem Caesar Verfahren alles,
was zwischen A-Z liegt und dies auch mit der gewünschten Verschiebung. Nur haben wir große Probleme,
beim Einfügen der Umlaute (äüö) sowie des ß.
Wir haben es bereits mit der ASCII versucht, allerdings stören uns die verbleibenden Sonderzeichen und restlichen Umlaute,
da wir auch rückübersetzen wollen und dass so dann natürlich nicht so recht funktioniert, da wir nicht umkopieren können.
Zudem finden wir es nicht ansehnlich und möchten es vermeiden, kamen aber leider nicht auf eine andere Lösung
und diverse Internetbeiträge halfen uns auch nicht wirklich weiter.

Hier ist nun unser Code (in Komplettfassung) :

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;

/**
 *
 * Beschreibung
 *
 * @version 1.0 vom 07.10.2015
 * @author
 */

public class CaesarSatan extends JFrame {
    // Anfang Attribute
    private JTextField jTextField1 = new JTextField();
    private JTextField jTextField2 = new JTextField();
    private JButton jButton1 = new JButton();
    private JNumberField jNumberField1 = new JNumberField();
    private JButton jButton2 = new JButton();
    private JLabel jLabel1 = new JLabel();
    private JLabel jLabel2 = new JLabel();
    private JLabel jLabel3 = new JLabel();
// Ende Attribute

    public CaesarSatan(String title) {
// Frame-Initialisierung
        super(title);
        setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
        int frameWidth = 296;
        int frameHeight = 300;
        setSize(frameWidth, frameHeight);
        Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
        int x = (d.width - getSize().width) / 2;
        int y = (d.height - getSize().height) / 2;
        setLocation(x, y);
        setResizable(false);
        Container cp = getContentPane();
        cp.setLayout(null);
// Anfang Komponenten

        jTextField1.setBounds(72, 48, 150, 20);
        cp.add(jTextField1);
        jTextField2.setBounds(72, 208, 150, 20);
        cp.add(jTextField2);
        jButton1.setBounds(24, 152, 99, 25);
        jButton1.setText("Verschlüsseln");
        jButton1.setMargin(new Insets(2, 2, 2, 2));
        jButton1.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                jButton1_ActionPerformed(evt);
            }
        });
        jButton1.setBackground(new Color(0xA3B8CC));
        cp.add(jButton1);
        jNumberField1.setBounds(104, 104, 83, 20);
        jNumberField1.setText("");
        cp.add(jNumberField1);
        jButton2.setBounds(168, 152, 99, 25);
        jButton2.setText("Entschlüsseln");
        jButton2.setMargin(new Insets(2, 2, 2, 2));
        jButton2.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                jButton2_ActionPerformed(evt);
            }
        });
        jButton2.setBackground(new Color(0xA3B8CC));
        cp.add(jButton2);
        jLabel1.setBounds(8, 48, 62, 20);
        jLabel1.setText("Botschaft:");
        cp.add(jLabel1);
        jLabel2.setBounds(8, 104, 86, 20);
        jLabel2.setText("Verschiebung:");
        cp.add(jLabel2);
        jLabel3.setBounds(8, 208, 62, 20);
        jLabel3.setText("Botschaft:");
        jLabel3.setOpaque(false);
        cp.add(jLabel3);
        cp.setBackground(new Color(0xB8CFE5));
// Ende Komponenten

        setVisible(true);
    } // end of public CaesarSatan

    // Anfang Methoden
    public void jButton1_ActionPerformed(ActionEvent evt) {
        String geheimtext = "";
        String klartext= jTextField1.getText();
        int verschiebung = jNumberField1.getInt();
        char buchstabe;
        int nummer;
        klartext = klartext.toUpperCase();
        for (int i=0; i < klartext.length(); i++)
        { buchstabe = klartext.charAt(i);
            nummer = (int)buchstabe-(int)'A';
            if (buchstabe >='A' && buchstabe <='Z'){
                nummer = (nummer + verschiebung + 26) % 26;
            }
            geheimtext = geheimtext + (char)(nummer + (int)'A');
        }
        jTextField2.setText(geheimtext); // TODO hier Quelltext einfügen

    } // end of jButton1_ActionPerformed

    public void jButton2_ActionPerformed(ActionEvent evt) {
        String geheimtext = "";
        String klartext= jTextField1.getText();
        int verschiebung = 26-jNumberField1.getInt();
        char buchstabe;
        int nummer; klartext = klartext.toUpperCase();
        for (int i=0; i < klartext.length(); i++)
        { buchstabe = klartext.charAt(i);
            nummer = (int)buchstabe-(int)'A';
            if (buchstabe >='A' && buchstabe<='Z')
            { nummer = (nummer + verschiebung + 26) % 26; }
            geheimtext = geheimtext + (char)(nummer + (int)'A');
        }
        jTextField2.setText(geheimtext);// TODO hier Quelltext einfügen
    } // end of jButton2_ActionPerformed

// Ende Methoden

    public static void main(String[] args) {
        new CaesarSatan("CaesarSatan");
    } // end of main

} // end of class CaesarSatan

****Wichtig sind in diesem Fall sicherlich nur die Dinge, die wir in die Buttons programmiert haben.
Wir würden uns sehr über hilfreiche Kommentare freuen, da es bei uns im Informatik Unterricht
eine eher weniger lehrreiche Phase gab und uns deshalb der letzte Schritt sehr schwer fällt.

Mit freundlichen Grüßen,
zwei verzweifelte Schülerinnen <3****

Bitte Java-Tags für Code verwenden!

Das Problem ist natürlich, dass die Umlaute und ß nicht im gleichen Bereich liegen wie die “normalen” Buchstaben.

Dafür gibt es dafür mehrere Lösungsmöglichkeiten. Man könnte z.B. alle “erlaubten” Buchstaben in irgend einer Reihenfolge in einen String packen "abcdef...xyzäöüßABCDE...XYZÄÖÜ", und müsste dann für jeden zu verschlüsselnden Buchstaben erst einmal die Position im String ermitteln (z.B. mit indexOf), den Rotationswert dazuaddieren und mit % wieder “umwrappen” (falls er zu groß ist), und dann den verschlüsselten Buchstaben aus dem String holen, z.B. mit charAt.

Bei solchen Problemen sollte man übrigens immer so einfach wie möglich anfangen und das eigentliche Problem lösen. Also erst mal ohne Oberfläche, sondern über die Konsole. Ich würde es so schreiben (nur Kleinbuchstaben, kann um beliebige Buchstaben erweitert werden):

public class Test {

    private static final String letters = "abcdefghijklmnopqrstuvwxyzäöüß ";
    private static final int rotation = 13;

    private static String code(String s, int offset) {
        StringBuilder sb = new StringBuilder();
        for(char c : s.toCharArray()) {
            int index = letters.indexOf(c);
            if (index < 0) {
                sb.append('?'); //unbekannter Buchstabe
            } else {
                int position = (index + offset) % letters.length();
                sb.append(letters.charAt(position));
            }
        }
        return sb.toString();
    }

    public static String encode(String s) {
        return code(s, rotation);
    }

    public static String decode(String s) {
        return code(s, letters.length() - rotation);
    }

    //nur zum Testen
    public static void main(String[] args) {
        String s = encode("ein maß bier und ein käsebrot");
        System.out.println(s);
        String t = decode(s);
        System.out.println(t);
    }
}

Es wäre auch eine gute Idee, die Codierung/Decodierung in einer anderen Klasse vorzunehmen als die Anzeige der Oberfläche - das sind verschiedene Aufgaben (“Separation of Concerns”)

Vielen Dank schon mal!
Wie genau funktioniert das denn mit dem indexOf, wir haben leider keine Ahnung…

Moin,

[QUOTE=HasiBernd]
Wie genau funktioniert das denn mit dem indexOf, wir haben leider keine Ahnung…[/QUOTE]
hierzu bitte einfach ein Blick in die entsprechende API werfen:
Java Platform SE 7

Gruß Klaus

Kann man alles in der API-Doc nachlesen:

indexOf(int ch)

Returns the index within this string of the first occurrence of the specified character.

https://docs.oracle.com/javase/8/docs/api/java/lang/String.html

Also etwa „liefert den Index in diesem String, wo der spezifizierte Buchstabe zuerst auftaucht“

Der GUI-Code ist in Ordnung.


/**
 * @author CB
 */
public class Temp {

    private static final String alpha;

    static {
        StringBuilder sb = new StringBuilder();
        for (char ch = 'A'; ch <= 'Z'; ch++) {
            sb.append(ch);
            sb.append(Character.toLowerCase(ch));
        }
        sb.append("ÄÖÜßäöüß ");
        alpha = sb.toString();
    }

    public static void main(String[] args) {
        System.out.println("Halloß");
        System.out.println(encodieren("Halloß", 3));
        System.out.println(decodieren(encodieren("Halloß", 3), 3));
    }

    private static String encodieren(String string, int off) {
        String string2 = "";
        for (int i = 0; i < string.length(); i++) {
            string2 += alpha.charAt((alpha.indexOf(string.charAt(i)) + off) % alpha.length());
        }
        return string2;
    }

    private static String decodieren(String string, int off) {
        return encodieren(string, -off);
    }
}```

run:
Halloß
iCNNQü
Halloß
BUILD SUCCESSFUL (total time: 0 seconds)



... könntet ihr jetzt in den GUI-Code einfügen.

*** Edit ***

Sorry, ich hab voll den Quatsch gebaut,

decodieren:
```    private static String decodieren(String string, int off) {
        String string2 = "";
        for (int i = 0; i < string.length(); i++) {
            int off2 = alpha.indexOf(string.charAt(i)) - off;
            while (off2 < 0) {
                off2 += alpha.length();
            }
            string2 += alpha.charAt(off2);
        }
        return string2;
    }```

das kommt, wenn man alles in eine Zeile packen will...

ein ß (Eszett) ist auch zu viel...

Für `Integer.MAX_VALUE` funktioniert es trotzdem nicht, vielleicht doch in mehrere Zeilen aufteilen, und dann alles zusammenfassen, aber das ist ja Ihre aufgabe.

prinzipiell muss der komplette Wertebereich -... bis +... sinnvoll auf die Länge Alphabet aufgeteilt werden, die Erfinder, Römer, hatten dazu eine Scheibe, das etwas einfacher.

(heißt de- und encodieren nicht dasselbe?)

da hat Landei schon

        return code(s, letters.length() - rotation);
    }

lange vor dir geschrieben,
deine Variante

        return encodieren(string, -off);
    }

wäre zunächst ähnlich, evtl. auch ok wenn das % nur ausreichend funktionieren würde, einmal length() addieren sollte aber in jedem Fall reichen,
aber dann musst du nun den kompletten Code kopieren mit while-Schleife drin

nebenher auch noch gefährlich String + String zusammengebaut statt des schon bei Landei vorgegeben StringBuilders,
bei großen Texten dauert das endlos länger (wichtig @HasiBernd )

insgesamt mal wieder alles verkorkst, nichtmal unnötig spammend nur das korrekte schon vorhandene kopiert,
sondern noch schlechter nachgebaut, wie immer eine Gefahr für alle und große Warnung, diese Postings von User CyborgBeta zu lesen,

ich würde sie ja löschen und weiteres, aber da zu deinem Glück die anderen Moderatoren gemütlich zuschauen darfst du dich auf diese Weise weiter verbreiten…

Nein, da kann auch einiges verbessert werden.

Angefangen von der Benennung: Was ist z.B. jTextField1? Das ist kein vernünftiger Bezeichner (kommt wohl alles aus einem GUI-Editor, aber das ist kein Grund, das so stehen zu lassen).

Dann ganz, ganz böse das Null-Layout. Ist leicht zu schreiben und liefert schnelle Ergebnisse, ist aber so gut wie nicht wartbar, und kann natürlich nicht mit Größenänderungen umgehen (was gerade eine der Stärken von Swing ist). Die LayoutManager in Swing bieten viele Möglichkeiten, auch wenn man manchmal etwas nachdenken (und auch mehrere schachteln) muss. In größeren Projekten kann man auch „fremde“ Layouts wie FormLayout oder TableLayout einsetzen (was hier allerdings Overkill wäre).

Der Konstruktor ist deutlich zu lang, sollte logisch in Unter-Methoden aufgeteilt werden.

Von JFrame zu erben ist normalerweise unnötig (Stichwort „Composition over Inheritance“, also Komposition der Vererbung vorziehen, wenn möglich).

Den Code von CyborgBeta bitte mit sehr, sehr viel Vorsicht genießen - er meint es zwar gut, verschlimmbessert aber oft Probleme und hat sich als völlig lernresistent erwiesen.

@CyborgBeta wie immer ein sehr verständlicher Post mit vielen tollen und gut durchdachten Variablennamen

Was jedoch eventuell, um deine wirren Gedanken mal weiter zu spinnen, also deine 1. Variante, alles in einem zu machen. Wenn man beim codieren deinen Code 1.Variante mit einem Shift von 2 aufruft, müsste man einfach zum dekodieren mit alpha.length()-2 aufrufen

dein modifizierter code PS: ich habe nicht deine Wohldurchdachten Variablennamen&co geändert
[spoiler]

private String alpha;

    public void init() {
        StringBuilder sb = new StringBuilder();
        for (char ch = 'A'; ch <= 'Z'; ch++) {
            sb.append(ch);
            sb.append(Character.toLowerCase(ch));
        }
        sb.append("ÄÖÜßäöüß ");
        alpha = sb.toString();
    }

    @Test
    public void testDecode() {
        init();
        System.out.println("Wenn jetzt hier ein komplizierter Text stünde");
        System.out.println(encodieren("Wenn jetzt hier ein komplizierter Text stünde", 3));
        System.out.println(decodieren("xGPPBLGVÜVBJKGTBGKPBMQORNKÜKGTVGTBuGZVBUVAPFG", 3));
    }

    private  String encodieren(String string, int off) {
        String string2 = "";
        for (int i = 0; i < string.length(); i++) {
            string2 += alpha.charAt((alpha.indexOf(string.charAt(i)) + off) % alpha.length());
        }
        return string2;
    }

    private  String decodieren(String string, int off) {
        return encodieren(string, alpha.length()-off);
    }

[/spoiler]

In der Aufgabestellung muss auch vereinbart werden, welche Werte für den „Schlüssel“ denn nun zugelassen sind.

0 - 1 = -1 = 26 - 1 = 25

Aber Slater hat recht, das % (Modulo) ist unzureichend implementiert.

[OT]Ich möchte auch ein Bier. :heul:[/OT]

*** Edit ***

Sorry, irgendwie stehe ich heute auf dem Schlauch, denn/oder ich sehe die Änderung in Zeile 25 (deine Variante) nicht.

dann schau mal in Zeile 31 wo dekodiert wird (schon ärgerlich wenn man nicht durch seinen eigenen Code durchblickt [und das obwohl da die stelle noch Sinnvoll benamet ist, da heißen die Methoden mal nach dem was sie machen, dekodieren und encodieren])
Ansonsten schau Dir einfach mal das Beispiel von @Landei an, das ist mal deutlich schöner programmiert als dein Code @CyborgBeta , da kannst du noch was von lernen. Hätte ich den Code dort oben genauer angeschaut, hätte ich meine Anmerkungen gelassen, da ja da schon ordentlicher Code war und dein wirrer Code somit nicht mehr relevant gewesen wäre

Hier ist das nochmal abgeändert/überarbeitet:

Code:
[spoiler]```package javaapplication;

/**

  • @author CB und andere, 04.11.
    */
    public class Temp {

    private static String alphabet;

    private static void init() {
    StringBuilder sb = new StringBuilder();
    for (char ch = ‘A’; ch <= ‘Z’; ch++) {
    sb.append(ch);
    sb.append(Character.toLowerCase(ch));
    }
    sb.append("ÄÖÜßäöü ");
    alphabet = sb.toString();
    }

    public static void main(String[] args) {
    init();
    String s = “hIer stünden ein sehr Komplizierter text, welcheß vom Erfinder aber fälsch geschrieben wurde bewußt, weihl dießer ein Bieör häben möchte”;
    int schluessel = 77;
    System.out.println(s);
    System.out.println(encodieren(s, schluessel));
    System.out.println(decodieren(encodieren(s, schluessel), schluessel));
    }

    private static String codieren(String string, int offset) {
    String string2 = “”; /* StringBuilder */
    for (int i = 0; i < string.length(); i++) {
    string2 += alphabet.charAt((alphabet.indexOf(string.charAt(i)) + offset) % alphabet.length());
    }
    return string2;
    }

    private static String encodieren(String string, int offset) {
    return codieren(string, offset);
    }

    private static String decodieren(String string, int offset) {
    return encodieren(string, alphabet.length() - (offset % alphabet.length()));
    }
    }```

run:
hIer stünden ein sehr Komplizierter text, welcheß vom Erfinder aber fälsch geschrieben wurde bewußt, weihl dießer ein Bieör häben möchte
QqNÄIÜähWMNWINRWIÜNQÄIsXVYURERNÄäNÄIäNCäIIBNULQNGIAXVImÄORWMNÄIJKNÄIOgUÜLQIPNÜLQÄRNKNWIBüÄMNIKNBüGäIIBNRQUIMRNGNÄINRWIjRNHÄIQgKNWIVHLQäN
hIer stünden ein sehr Komplizierter text  welcheß vom Erfinder aber fälsch geschrieben wurde bewußt  weihl dießer ein Bieör häben möchte
BUILD SUCCESSFUL (total time: 0 seconds)

[/spoiler]

Danke alle für Eure Hinweise.

na klar, für den begrenzten Zusammenbau alphabet/ letters, für den Landei keinen StringBuilder hatte, da baust du StringBuilder ein,
für den womöglich MB-großen zu kodieren String, wo Landei StringBuilder hatte, da wo es wichtig ist, da machst du weiterhin String + String…

perfekte Darstellung von Nicht anderen Code lesen, Nicht selber nachdenken und generell Alles so falsch wie möglich zu machen, schön trollig

Bis auf den StringBuilder und die Vereinbarung über den Wertebereich ist nun alles ok. Ein größerer Wertebereich macht die Kodierung aber auch nicht sicherer, btw.

Womöglich GB-groß, dann kommt StringBuilder aber auch an sein Limit, dann wäre auch ein String und Array auch nicht passend. Wahrscheinlich hätte ich dann in Assembler getestet, bei der vermeintlich einfachen Aufgabe wird es dann sehr kompliziert, auch Parallelisierung und Synchronisierung möglich.

Bilde -Integer.MAX_VALUE bis +Integer.MAX_VALUE so auf 0 bis n-1 ab, dass Verschoben um diese Abbildung und wieder Verschoben um -diese Abbildung wieder den Text ergibt. Das wäre die Aufgabe.

Musst du partout immer das letzte Wort haben?
Und Nein dein Code ist auch wenn du den StringBuilder nimmst,… noch nicht schön, wobei die Anmerkung von @SlaterB zu recht ist, das du für einen Konstant kurzen Text eine StringBuilder verwendest aber für einen möglicherweise längeren Text verwendest du einfache Stringconkatination und hast nur als Kommentar dahinter geschrieben, das man es eventuell auch mittels StringBuilder lösen könnte.
Wenn ich einen längeren Text hätte, würde ich den wahrscheinlich aus einer Datei lesen und in diese wieder schreiben und da könnte man dann theoretisch jeden einzelnen Buchstaben lesen und dann wieder schreiben ohne den kompletten Text im Cache zu halten. Somit bekommt man da dann auch keine Probleme (außer natürlich Zeit) da diese Schreibzugriffe natürlich langsam wären. Solang man aber mit einfachen kurzen Beispielen Arbeitet macht der StringBuilder schon das was man will :wink:
Nur als Beispiel für eine unschöne / unleserliche Zeile

sb.append(ch);

Aber wenigstens die Methoden hast du mal schöne / sprechende Namen gegeben, was für dich ja schon eine deutliche Steigerung ist

Bist du denn Professor für Mathematik und kannst etwas Neues in die Diskussion bringen? Ne, oder?

Erklärt mir Bitte mal folgendes komische Verhalten:


/**
 * @author CB und andere, 04.11.2015
 */
public class Temp {

    private static String alphabet;

    private static void init() {
        StringBuilder sb = new StringBuilder();
        for (char ch = 'A'; ch <= 'Z'; ch++) {
            sb.append(ch);
            sb.append(Character.toLowerCase(ch));
        }
        sb.append("ÄÖÜßäöü .:,;-_");
        alphabet = sb.toString();
    }

    public static void main(String[] args) {
        init();
        System.out.println("alphabet = " + alphabet);
        System.out.println(0 % 15);
        String s = "hIer stünden ein sehr Komplizierter text, welcheß vom Erfinder aber fälsch geschrieben wurde bewußt, weihl dießer ein Bieör häben möchte";
        int schluessel = 77;
        System.out.println(s);
        System.out.println(encodieren(s, schluessel));
        System.out.println(decodieren(encodieren(s, schluessel), schluessel));
    }

    private static String codieren(String string, int offset) {
        StringBuilder builder = new StringBuilder(string.length());
        for (int i = 0; i < string.length(); i++) {
            int i1 = alphabet.indexOf(string.charAt(i)) - offset;
            if (i1 < 0) {
                i1 = i1 % (alphabet.length() - 1) + alphabet.length();
            } else if (i1 >= alphabet.length()) {
                i1 = i1 % alphabet.length();
            }
            builder.append(alphabet.charAt(i1));
        }
        return builder.toString();
    }

    private static String encodieren(String string, int offset) {
        return codieren(string, offset);
    }

    private static String decodieren(String string, int offset) {
        return encodieren(string, -offset);
    }
}```

alphabet = AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZzÄÖÜßäöü .:,;-_
0
hIer stünden ein sehr Komplizierter text, welcheß vom Erfinder aber fälsch geschrieben wurde bewußt, weihl dießer ein Bieör häben möchte
Cc;MYNOxI:;IY;DIYN;CMYeJHKGDUD;MO;MYO;SOzYR;G C;WYQJHY,M_DI:;MYßö;MY_wGN CYB;N CMD;ö;IYRPM:;Yö;RPWOzYR;DCGY:D;W;MY;DIYäD;XMYCwö;IYHX CO;
hIEr stünDEn Ein sEhr KompliziErtEr tExt, wElChEß vom drFinDEr ABEr FälsCh gEsChriEBEn wurDE BEwußt, wEihl DiEßEr Ein aiEör häBEn möChtE



1. ... Methode funktionieren für Werte zwischen 0..66
2. ... Methode funktionieren für Werte zwischen 67..
3. ... Methode funktionieren für Werte bis Integer.MAX_VALUE

Ok, wir streiten uns ja nur noch um Details wie Namen.

Gestern hab ich gesehen, dieses Thema ist buchstäblich/wortwörtlich ein Crosspost im altem jforg.

hust - 5 1/2 Phasen und so …

http://forum.byte-welt.net/sonstiges/hausaufgaben/4645-die-5-1-2-phasen-beim-erschleichen-von-loesungen-fuer-aufgaben.html

Es ist eh ein so genanntes Crossposting. Alles was jetzt kommt, kann es nur verschlimmern, Sen.