C# Konsolen-Taschenrechner - String parsen

ohbai

Newbie
Registriert
Dez. 2013
Beiträge
2
Hallo zusammen,

ich brauche Hilfe beim Umsetzung einer neuen Funktion in meinem Taschenrechner.

Und zwar habe ich bisher ein Konsolen-Programm geschrieben, dass aus zwei eingegeben Strings eine dritte Zahl berechnet mit nur einem Operator. Nun will ich jedoch die Eingabe und Ausgabe der Daten erweitern, indem ich die Eingabe auf einen einzigen String reduziere und dann entsprechend parse. Dabei sollen auch mehrere Operatoren (+, -, *, / ) vorkommen können. Ich habe mir bereits den Rangierbahnhof-Algorithmus angeschaut, allerdings nur Wurstsalat verstanden.

Über die gängigen Suchmaschinen habe ich bisher nur viel zu komplexe Code-Beispiele gefunden, die mit Sinus / Konsinus, regulären Ausdrücken, etc. gearbeitet haben. Ich suche jedoch eine relativ simple und leicht nachvollziehbare Lösung dafür.

Das habe ich bisher verbrochen:
Code:
static void Main()
        {
            double eingabe01 = 0;
            double eingabe02 = 0;

            char pOperator = '0';
            bool success = true;

            string hinweisExit = "\n\nHinweis: Das Programm wird nun beendet.";
            string hinweisExitUser = "\n\nHinweis: Das Programm kann nun mit Enter beendet werden.";

            //
            Console.WriteLine("Bitte geben Sie nun Ihre erste Zahl ein!");

            try
            {
                eingabeZahl01 = Convert.ToDouble(Console.ReadLine());
            }//try
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                success = false;
                //System.exit(0); -> wurde ersetzt durch if-else Abfragen
            }//catch
            if (success != false)
            {
                Console.WriteLine("\nWas für einen Operator möchten Sie benutzen?");
                Console.WriteLine("* = multiplizieren |  + = addieren | - = subtrahieren | / = dividieren");
                try
                {
                    mOperator = Convert.ToChar(Console.ReadLine());
                    //success = false;
                }//try
                catch (Exception e)
                {
                    Console.WriteLine(e.Message);
                    success = false;
                    //System.exit(0); -> wurde ersetzt durch if-else Abfragen
                }//catch
                if (success != false)
                {
                    switch (mOperator)
                    {
                        case '*': Console.WriteLine("\nErgebnis: " + (eingabeZahl01) + " * " + (eingabeZahl02) + " ist: " + (eingabeZahl01 * eingabeZahl02) + hinweisExitUser); break;
                        case '+': Console.WriteLine("\nErgebnis: " + (eingabeZahl01) + " + " + (eingabeZahl02) + " ist: " + (eingabeZahl01 + eingabeZahl02) + hinweisExitUser); break;
                        case '-': Console.WriteLine("\nErgebnis: " + (eingabeZahl01) + " - " + (eingabeZahl02) + " ist: " + (eingabeZahl01 - eingabeZahl02) + hinweisExitUser); break;
                        case '/':
                            if (eingabeZahl02 != 0)
                            {
                                Console.WriteLine("\nErgebnis: " + (eingabeZahl01) + " / " + (eingabeZahl02) + " ist: " + (eingabeZahl01 / eingabeZahl02) + hinweisExit);
                            }//if
                            else
                            {
                                Console.WriteLine("\nSie haben soeben versucht durch 0 zu teilen." + hinweisExit);
                            }//else
                            break;
                        default: Console.WriteLine("\nError: Sie dürfen nur die oben genannten Operatoren eingeben.\n\nHinweis: " + hinweisExit); break;
                    }//switch
                    Console.ReadLine();
                }//if success false
                else
                {
                    Console.WriteLine(hinweisExit);
                    System.Threading.Thread.Sleep(5000); //ersetzt: Console.ReadLine();
                }//else
            }//if success false
            else
            {
                Console.WriteLine(hinweisExit);
                System.Threading.Thread.Sleep(5000); //ersetzt: Console.ReadLine();
            }//else 
}//static void Main

Würde mich über Hilfe freuen.
Grüße
 
Hallo,

als erstes solltest du dich einem einfacherem Problem zuwenden:

1. auf einen regulären Klammerausdruck zu prüfen ;).
2. nun +-... implementieren aber ohne Operatorenreihenfolge und korrekter Klammerung z.B. (3+5)-4
3. wie könnte man von einer infix zu einer präfix Notation kommen?
3. wie kann man jene schnell durchrechnen?
4. wie kann man dann die operatorenreihenfolge beachten?

zu ersten kann ich dir ein Tipp geben:
Das kann man mit nem Stack schaffen ;)
 
Eigentlich total simpel. Du baust aus deinem Ausdruck einen Baum und arbeitest den dann rekursiv ab. MSPAINTSKILLS ZUR RETTUNG!

eval.png


Den Baum baust du, indem du den Ausdruck duchgehst und feststellst, welcher Art er ist, wenn man ihn so oberflächlich wie möglich betrachtet.
Im Beispiel wäre es eine Differenz, aber man könnte den Ausdruck auch als Summe sehen, da Addition und Subtraktion am Ende das Selbe sind,
ist es hier egal.

Der Subtraktionsoperator wird der Wurzelknoten und der linke und rechte Teilausdruck (2 + 3 * 4 sowie 8) die Childknoten. Dann fährt man mit den
Teilausdrücken fort, bis der Ausdruck komplett zerlegt ist. Am Ende arbeitet man den Baum dann rekursiv ab.

€: Abarbeiten des Baumes

1. Eine Zahl wird zu ihrem Wert evaluiert.
2. Ein Operator wird zu seiner Anwendung auf den linken und den rechten Teilbaum evaluiert.
3. Fertig.

Im Beispiel:

1. Der Wurzelknoten wird evaluiert zu linkerteilbaum1 MINUS rechterteilbaum1
2. rechterteilbaum1 ist eine Zahl und wird zum Wert 8 evaluiert.
3. linkerteilbaum1 ist ein Operator und wird zu linkerteilbaum2 PLUS rechterteilbaum2 evaluiert.
4. linkerteilbaum2 ist eine Zahl und wird zum Wert 2 evaluiert.
5. rechterteilbaum2 ist ein Operator und wird zu linkerteilbaum3 MAL rechterteilbaum3 evaluiert.
6. linkerteilbaum3 = 2; rechterteilbaum3 = 3
7. rechterteilbaum2 = 2 * 3 = 6
8. linkerteilbaum1 = 4 + 6 = 10
9. Wurzel = 10 - 8 = 2
10. Jetzt erstmal ein Bierchen.
 
Zuletzt bearbeitet:
ohbai schrieb:
Ich suche jedoch eine relativ simple und leicht nachvollziehbare Lösung dafür.

Mein Tipp ist, sich das Problem in Teilprobleme zu zerlegen, die einfacher lösbar sind.

Angenommen, dein Taschenrechner bekommt das zu rechnen: "2*3"
Das übergeordnete Problem ist: "Was ist das Ergebnis?"
Die Teilprobleme sind: "Was ist die erste Zahl?"; "Was ist die zweite Zahl?"; "Welcher Operator wird verwendet?"; "Wie wende ich den Operator auf beide Zahlen an?"

Wenn man sich jetzt die Teilprobleme anschaut, kommt man auf den Gedanken, den String "2*3" zu zerlegen. Am besten wäre "2", "*" und "3". Was habe ich hier gemacht? Ich habe den Operator gesucht, und vor und nach ihm den String zerlegt. Dadurch habe ich 3 der 4 Teilprobleme schon gelöst. Ich kann sagen, welche Zahlen und welchen Operator es gibt.

Jetzt muss ich nur noch auswerten, welcher Operator das ist und schließlich die Rechnung durchführen.


RegEx ist in diesem Fall schön, da man da auf recht einfache Weise Strings wie "2+45", "62*3" usw. in Zahl- und Operator-Strings zerlegen kann. So was wie (\d+)([+|-|*|/)(\d+) passt hier gut. (\d+) steht für Dezimalzeichen (\d), mit mindestens einer Ziffer (+). ([+|-|*|/) steht dafür, dass genau einer der vier Operatoren vorkommen muss. Der RegEx matched also auf eine Ziffernfolge, gefolgt von einem der vier Operatoren, gefolgt von einer weiteren Ziffernfolge.
"654/65" zerlegt er in ein String-Array mit den Elementen "654", "/" und "65", also ziemlich genau so, wie du es brauchst.
 
Zuletzt bearbeitet:
Der Ansatz mit dem Baum ist gut, so habe ich das auch schon gemacht. Klammern lassen sich auch noch leicht implementieren. Kann heute Abend zu hause auch mal nach dem Quellcode schauen.

Edit: Nicht in noch geändert....
 
Zuletzt bearbeitet:
Lösungsansätze gibt es ja schon reichlich hier, deswegen nur ganz kurz etwas nebenbei:

Es ist nicht der Sinn und Zweck von try Fehler des Nutzers abzufangen, oder anders ausgedrückt man sollte vermeiden Fehler auszuspucken, wenn es nicht wirklich nötig ist. In diesem Fall lässt sich das Problem mit der Falscheingabe ganz Einfach lösen.
Code:
double result;
if(!double.TryParse(Console.ReadLine(), out result)
{ \\ report to user... }
 
e-Laurin schrieb:
Mein Tipp ist, sich das Problem in Teilprobleme zu zerlegen, die einfacher lösbar sind.

Angenommen, dein Taschenrechner bekommt das zu rechnen: "2*3"
Das übergeordnete Problem ist: "Was ist das Ergebnis?"
Die Teilprobleme sind: "Was ist die erste Zahl?"; "Was ist die zweite Zahl?"; "Welcher Operator wird verwendet?"; "Wie wende ich den Operator auf beide Zahlen an?"

Wenn man sich jetzt die Teilprobleme anschaut, kommt man auf den Gedanken, den String "2*3" zu zerlegen. Am besten wäre "2", "*" und "3". Was habe ich hier gemacht? Ich habe den Operator gesucht, und vor und nach ihm den String zerlegt. Dadurch habe ich 3 der 4 Teilprobleme schon gelöst. Ich kann sagen, welche Zahlen und welchen Operator es gibt.

Jetzt muss ich nur noch auswerten, welcher Operator das ist und schließlich die Rechnung durchführen.

Eine schöne Erklärung, die vielleicht zugänglicher ist, als meine. Wenn man sie auf beliebige Ausdrücke erweitert (also dass die Operanden selbst zusammengesetzte Ausdrücke sein können) landet man bei meiner Baumlösung. Also für OP: Vielleicht unsere beiden Posts als gegenseitig ergänzend betrachten, statt als unterschiedliche Lösungen für dein Problem.
 
Hallo zusammen,

vielen Dank für eure zahlreiche Antworten. Es wundert mich schon - ehrlich gesagt -, dass es doch noch Foren gibt, in denen man einigermaßen freundlich behandelt wird und nicht jeder zweiter Post "Benutz Forensuche, du Arsch!" lautet. Daher also danke für eure Antworten.

Die Idee mit dem Baum-Aufbau vom asdfman (by the way: coole Strichmännchen ;D) klingt für mich schonmal ganz logisch nachvollziehbar und scheint für mich im Endeffekt auch leicht erweiterbar. Ich habe ja bereits im Startpost meinen bisherigen Programmcode gepostet und frage mich jetzt aktuell noch (da ich auch noch blutiger Anfänger was C# angeht bin) wie ich das programmieren soll. Womit sollte ich anfangen und auf welche Funktionen müsste ich zurückgreifen? Hat jemand vielleicht ein Code-Beispiel, damit ich das theoretische auch mal in der C#-Syntax sehen kann?

RegEx habe ich mir schonmal kurz angeschaut, allerdings erscheint mir hier die Syntax ziemlich schwer verständlich für einen Anfänger. Gibt es da nennenswerte Alternativen?

Die Sache mit try und catch, die PapstRatze erwähnt hat, war mir nicht bewußt. Habe mich nur gefreut, dass es das getan hat, was ich mir eigentlich dabei gedacht habe. Wenn es eine effizientere und einfachere Lösung gibt, dann werde ich das wie beschrieben mal umsetzen. Danke für den Hinweis!

Würde mich nochmal über Antworten freuen.
Grüße
 
Es scheint nicht so zu sein, dass du von Objektorientierung schon etwas verstehst. Wie weit reicht dein Wissensstand bis jetzt? Was hast du alles schon so programmiert (und was hast du dabei verwendet)?


RegEx ist das beste Mittel, um reguläre Ausdrücke zu beschreiben. Was sind reguläre Ausdrücke? Vereinfacht gesagt, ist das eine Ausdrucksform, um Mengen von Dingen nach bestimmten Regeln einzuteilen. Ok, jetzt hab ich das Fremdwort mit Fremdwörtern erklärt. ^^
Nehmen wir mal ein Beispiel, dann wird es anschaulicher: Stell dir ein Siegerpodest vor. Das hat üblicherweise drei verschieden hohe Stufen. Links ist der 2. Platz, in der Mitte ist der 1. Platz und rechts ist der 3. Platz. Man kann jetzt sagen, die drei besten stellen sich in der Reihenfolge 213 auf. Dann ist jeder auf der richtigen Stufe. Und zack, da haben wir es schon. 213 ist ein regulärer Ausdruck. Er beschreibt, nach welcher Regel sich die Menschen aufstellen müssen.

Über so was stolpert man in der Programmierung immer wieder. Nehmen wir an, du willst eine Textdatei nach einem bestimmten Wort durchsuchen. Du stellst dir dann in Gedanken normalerweise die Regel auf: Viel Babla, dann mein gesuchtes Wort, und dann wieder viel Blabla. BlablaSuchwortBlabla wäre dann hier der reguläre Ausdruck.

RegEx ist eine Sprache, mit der du genau solche Dinge ausdrücken kannst. Mir fällt auf Anhieb keine andere Sprache ein, in der so was mit so wenig Aufwand möglich ist. Ja, die Syntax erscheint oft einfach nur verwirrend. Auch ich muss immer wieder in die Doku schauen, wie man jetzt dies oder jenes formuliert. Das Ergebnis macht das aber wett.
Hier mal ein RegEx, das die Norm für E-Mail-Adressen fast vollständig umsetzt: http://www.ex-parrot.com/pdw/Mail-RFC822-Address.html
Ja, die Länge erscheint völlig übertrieben. Aber hey, die dazugehörige RFC (so was wie ne Norm) ist auch fast 50 Seiten lang. Versuch das mal in einer anderen Sprache umzusetzen. Da schreibst du ein Jahr lang dran.
 
Zuletzt bearbeitet:
Zurück
Oben