| Autor |
Beitrag |
EdiZ84
Hält's aus hier
Beiträge: 15
|
Verfasst: Do 07.05.09 16:33
hallo liebes Forum. Ich hatte vor einiger zeit angesprochen das ich ein Parser erstellen wollte allerdings noch überhaupt keine Kenntnisse in C# habe.
Nun habe ich mit dem Buch (Richtig einsteigen: Programmieren lernen mit C# 2005) schon angefangen und merke erste Fortschritte. Allerdings möchte ich wissen ob ich überhaupt das richtige für den Parser lerne. Ich bearbeite gerade das Kapitel "Zeichenfolgen bearbeiten".
-Es ist richtig die eingegebenen Funktion (2+5)*3 erstmal mit split zu trennen wie z.B.
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19:
| string eingabe3 = Console.ReadLine(); string[] aufgabe;
char[] trennzeichen = ; aufgabe = eingabe3.Split(trennzeichen, StringSplitOptions.RemoveEmptyEntries); Console.WriteLine(eingabe3);
for (int i = 0; i < aufgabe.Length; i++) Console.WriteLine(". Teilsting: --", i + 1, aufgabe[i]); Console.WriteLine("");
char[] myCharArr = eingabe3.ToCharArray(); for (int i = 0; i <= myCharArr.GetUpperBound(0); i++) Console.WriteLine(myCharArr[i]); Console.Read(); |
-Die Trennzeichen sind bei mir die Operatoren wie benutze ich diese jetzt wenn ich die Funktion ausrechnen will????
-Wie mache ich das mit der Punkt vor Strich-Rechnung ?????
-Gibt es ein Buch was speziell alles von Anfang(für Anfänger) bis zum Ende alles erklärt?????
Ich weis es wird in vielen Foren darüber diskutiert, aber da ich noch null Ahnung habe, verstehe ich den Code nicht.
Könnten ihr mir wenigstens sagen ob ich die Aufgabe richtig anpacke, oder bin ich ganz falsch???
Habt ihr irgendetwas über die parser Theorie????
Vielen Dank im Voraus.
Mfg
Edgar
Moderiert von Christian S.: Delphi-Tags hinzugefügt
|
|
Thorsten83
      
Beiträge: 191
Erhaltene Danke: 1
|
Verfasst: Do 07.05.09 19:13
Hey,
ich würde erstmal Formeln induktiv definieren:
1. x ist eine Formel (x ist eine Zahl)
2. Sind x und y Formeln, so ist (x op y) auch eine Formel, mit op € {+, -, *, /, ^}
Würde dann also prinzipiell so laufen:
Du gibst ein: ((2 + 5)*3)
Dein Programm sieht: Die Formel fängt mit einem "(" an, also kommen 2 Formeln, und ein Operand in der Mitte.
Also liest es x ein:
x fängt wieder mit "(" an, ist also wieder eine zusammengesetzte Formel.
Also wird erst x ausgelesen (2), dann der Operand (+), dann y(5), was du zu 7 auswerten kannst.
Dann gehst du zurück zum Auswerten der ersten Formel, das x hast du jetzt ja als 7 berechnet, als nächstes kommt der Operand, "*", und dann y, was einfach die 3 ist.
Dies wertest du aus zu 7*3 = 21, fertig
Etwas komplizierter wird es, wenn du
- 1 + 2 + 3 statt ((1 + 2) + 3)
- 1 * 2 + 3 statt ((1 * 2) + 3) eingeben willst, dann muss dein Programm halt auch wissen, in welcher Reihenfolge die Operanden auszuwerten sind, und dass eine Formel auch aus mehr als 2 Operanden bestehen kann.
Einfach ist dies z.B. in der polnischen Notation.
Ich hoffe, ich konnte dir einen kleinen Einblick geben 
|
|
Jakob_Ullmann
      
Beiträge: 1747
Erhaltene Danke: 15
Win 7, *Ubuntu GNU/Linux*
*Anjuta* (C, C++, Python), Geany (Vala), Lazarus (Pascal), Eclipse (Java)
|
Verfasst: Do 07.05.09 19:15
also mein Ansatz (der auch klappt) ist, das Teil rekursiv auseinanderzunehmen. Also bei 2*3+5 wird zuerst nach + gesucht, der Teil davor wird ausgewertet und der danach ebenso, die Rückgabewerte werden addiert. Bei 2*3 ist weder + noch - vorhanden, deshalb wird nach * gesucht und der Teil links davon (2) und der rechts (3) werden ausgewertet und die Ergebnisse multipliziert. Und so verzweigt sich das immer mehr, bis man einen String nicht weiter zerlegen kann, weil er eine bloße Zahl ist.
Klammern sind etwas anspruchsvoller, gehen aber im Prinzip genauso.
|
|
Thorsten83
      
Beiträge: 191
Erhaltene Danke: 1
|
Verfasst: Do 07.05.09 19:23
Was macht dein Parser bei nicht assoziativen Operationen?
edit: Erst denken, dann schreiben 
|
|
EdiZ84 
Hält's aus hier
Beiträge: 15
|
Verfasst: Do 07.05.09 20:42
Hi Leute, war kurz arbeiten.
Erstmal danke für die schnelle Hilfe. Aber genau bei diesen Problem komme ich ja nicht weiter. Wie teile ich den String in die Einzel-String richtig und wie benutzte ich dann die Operatoren?????
Mein Anfang:
und zwar, lese ich erstmal irgendeine Gleichung mit Console.Readline ein. z.B. (5+5)*2. Dann teile ich die Aufgabe in die Einzel-Stings (Die Trennzeichen sind die Operatoren (siehe Code)(Ist das Richtig????). Die einezelnen Teile werden in ein Array gespeichert, auf die ich auch zugreifen kann. Aber ich kann nicht auf die Operatoren zugreifen. Wie mache ich das????? wie kann ich auf die einzelnen Operatoren zugreifen. Ihr könnt ja mein Code ausprobieren und mir dann was dazu schreiben. (Dann sieht ihr auch das ich noch nicht so weit bin).
Wäre euch echt sehr verbindlich wenn ihr mir einen Beispiel-Code in C# schicken könntet. Nur einen Code der nachvollziehbar ist, wo eine Gleichung in die Einzelteile zerlegt wird und wo dann die Operatoren für die Rechnnung benutzt werden.
Mein Ziel ist es die Sachen zu verstehen, und nicht mit fertigen Codes zu arbeiten die andere erstellt haben.
Brauche ein Codebeispiel zu lernen.
Das mit dem Buch(Ich meine ein spezielles C# Buch für Mathematische Gleichungen.)
Danke im Voraus.
mfg Edgar
Ciao
|
|
Thorsten83
      
Beiträge: 191
Erhaltene Danke: 1
|
Verfasst: Do 07.05.09 22:47
Bin gerade dabei wieder eine VM mit VS aufzusetzen, wenn das nicht zu lange dauert mach ich schnell noch was fertig...
edit: Ich bezweifle, dass es ein Buch über Gleichungen in C# gibt... Meiner Meinung nach sind das Verständnis für die Modelierung solcher Probleme und die Kenntnis einer speziellen Sprache zwei Paar Schuhe...
|
|
EdiZ84 
Hält's aus hier
Beiträge: 15
|
Verfasst: Do 07.05.09 22:56
Hi Thorsten, ich danke dir auf jeden Fall für deine Mühe.
Bitte um Verständnis, hab vorher nur mit vba in excel was gemacht und das erst seit ein Jahr, hab echt nur einwenig Kenntnisse. Beschäftige mich auch erst seit einen Monat mit c#.
Brauche den Parser für meine Ausbildung. Wenn ich das schaffe bekomme ich diese. Daher will ich das auch (zum Teil) selbst erledigen.
Nochmal besten Dank.
|
|
Thorsten83
      
Beiträge: 191
Erhaltene Danke: 1
|
Verfasst: Fr 08.05.09 01:03
Hey,
nachdem die Installation Stunden (blödes Mergen...)gedauert hat hier eine schnell zusammengeklatschte Version.
Btw.: Irgendwie konnte ich nicht mit Pointern arbeiten, wisst ihr wo man das in VS2008 umstellen kann?
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78:
| class Program { int result = 0; int nextIndex = 0; void check(char[] letters) { if (nextIndex > letters.Length) { throw new Exception("Error parsing formula"); } } Boolean number(char c) { return c >= '0' && c <= '9'; } int read(char[] letters) { char c = letters[nextIndex]; if (number(c)) { return readNumber(letters); } if (letters[nextIndex] != '(') { Console.WriteLine("error parsing formula..."); return 0; } check(letters); nextIndex++; int op1 = read(letters); char op = letters[nextIndex++]; int op2 = read(letters); nextIndex++; switch (op) { case '+': return op1 + op2; case '-': return op1 - op2; case '*': return op1 * op2; case '/': return op1 / op2; default: return 0; } } int readNumber(char[] letters) { int result = 0; do { result *= 10; result += letters[nextIndex] - '0'; nextIndex++; } while (nextIndex < letters.Length && number(letters[nextIndex])); return result; } static void Main(string[] args) { Console.WriteLine("Formel eingeben..."); char[] letters = Console.ReadLine().ToCharArray(); Program p = new Program(); int result = p.read(letters); Console.Write(result); Console.ReadLine(); } } |
|
|
jaenicke
      
Beiträge: 19346
Erhaltene Danke: 1754
W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Fr 08.05.09 01:17
Um Pointer zu benutzen musst du den entsprechenden Codeblock oder die Methode als unsafe ausweisen, also public unsafe DoSomething() z.B., aber besser ist es einfach gar keine zu verwenden in C#. 
|
|
Kha
      
Beiträge: 3803
Erhaltene Danke: 176
Arch Linux
Python, C, C++ (vim)
|
Verfasst: Fr 08.05.09 11:17
EdiZ84 hat folgendes geschrieben : | Das mit dem Buch(Ich meine ein spezielles C# Buch für Mathematische Gleichungen.)
|
Terme, nicht Gleichungen. Ein ganzes Anfängerbuch dazu wirst du wie schon gesagt nicht finden, aber hier findest du einige Links zu Artikeln über das Thema.
Thorsten83 hat folgendes geschrieben : | | [...]hier eine schnell zusammengeklatschte Version. |
Da gibt es aber noch einiges zu tun - bei "1+1" fängts schon an  .
_________________ >λ=
|
|
Thorsten83
      
Beiträge: 191
Erhaltene Danke: 1
|
Verfasst: Fr 08.05.09 13:45
Nach obiger Definition ist 1+1 auch keine gültige Formel 
|
|
Jakob_Ullmann
      
Beiträge: 1747
Erhaltene Danke: 15
Win 7, *Ubuntu GNU/Linux*
*Anjuta* (C, C++, Python), Geany (Vala), Lazarus (Pascal), Eclipse (Java)
|
Verfasst: Fr 08.05.09 19:03
Thorsten83 hat folgendes geschrieben : | | Was macht dein Parser bei nicht assoziativen Operationen? |
Wie meinst du das?
|
|
EdiZ84 
Hält's aus hier
Beiträge: 15
|
Verfasst: Mo 11.05.09 13:09
Hi Leute erstmal besten dank, vorallen dingen dir Thorsten für den Code. Werde den aufjeden Fall studieren. Aber ich habe mal was komplett selber versucht, nach der Theorie von Jakob_Ullmann und weil so ein ähnliches Thema in meinen Buch behandelt wird.
Mein Ergebnis: (Funktioniert immoment nur wenn man die Gleichung "Zahl+Zahl*Zahl" eingibt.)
Wenn man dies eingibt zerteilt er alles komplett und findet auch die Operatoren.
Code:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99:
| using System; using System.Collections.Generic; using System.Linq; using System.Text;
namespace ConsoleApplication1 { class Program { static void Main(string[] args) { string[] aufgabe; char[] trennzeichen = { ' ', '+', '-', '(', ')', '/', ':', '*' }; string localpart, domainpart, localpart1, domainpart1 = ""; string ergebnis = "";
int zwischenergebnis = 0; int pos, pos1 = 0; int operand1, operand2, operand3, operand4 = 0;
Console.WriteLine("Geben Sie eine Gleichung ein."); string eingabe = Console.ReadLine(); aufgabe = eingabe.Split(trennzeichen, StringSplitOptions.RemoveEmptyEntries);
pos = eingabe.IndexOf("+"); localpart = eingabe.Substring(0, pos); domainpart = eingabe.Substring(pos + 1);
pos1 = domainpart.IndexOf("*"); localpart1 = domainpart.Substring(0, pos1); domainpart1 = domainpart.Substring(pos1 + 1);
operand1 = int.Parse(aufgabe[0]); char operant1 = eingabe[pos]; string aktion1 = operant1.ToString(); operand2 = int.Parse(aufgabe[1]);
char operant2 = domainpart[pos1]; string aktion2 = operant2.ToString(); operand3 = int.Parse(aufgabe[2]);
switch (aktion1) { case "+": zwischenergebnis = operand1 + operand2; break; case "-": zwischenergebnis = operand1 + operand2; break; case "*": zwischenergebnis = operand1 + operand2; break; case ":": case "/": zwischenergebnis = operand1 + operand2; break; default: ergebnis = "Fehler: ungültiger Operator!"; break; }
switch (aktion2) { case "+": ergebnis = "Ergebnis: " + (zwischenergebnis + operand3); break; case "-": ergebnis = "Ergebnis: " + (zwischenergebnis - operand3); break; case "*": ergebnis = "Ergebnis: " + (zwischenergebnis * operand3); break; case ":": case "/": ergebnis = "Ergebnis: " + (zwischenergebnis / operand3); break; default: ergebnis = "Fehler: ungültiger Operator!"; break; }
Console.WriteLine("1.Zerteilung"); Console.WriteLine(localpart); Console.WriteLine(domainpart); Console.WriteLine(aktion1); Console.WriteLine(zwischenergebnis);
Console.WriteLine("2.Zerteilung"); Console.WriteLine(localpart1); Console.WriteLine(domainpart1); Console.WriteLine(aktion2); Console.WriteLine(ergebnis);
Console.Read(); } } } |
Wenn ihr den Code mal ausprobiert zerteilt er alles in Einzelstrings, rechnet es auch(Punkt vor Strich-Rechnung wird noch nicht beachtet), und gibt die Ergebnisse aus.
Meine Fragen, dazu sind folgende:
Der erste Operator wird mit variable.indexof("+") gesucht. Geht das nicht anders???? z.B. so variable.indexof("operatorX")???
Das ist auch immoment das Problem, wenn ich beispielweise "zahl1-zahl2*33" eingebe gibt es eine Ausnahme.
Hab schon versucht es mit ner if else anweisung zu regeln, aber klappt nicht. z.B.
C#-Quelltext 1: 2: 3: 4: 5:
| if (eingabe.indexof("+") != 1) else if (eingabe.indexof("-") != 1) |
Leider klappt das nicht, weis nicht warum, ihr aber bestimmt.
Wie könnte ich das den anders gestalten??????
Falls ich mich nicht verständlich ausgedrückt habe, einmal kurz melden.
Ok, danke euch , ciao
Moderiert von Kha: C#-Tags hinzugefügt
|
|
Thorsten83
      
Beiträge: 191
Erhaltene Danke: 1
|
Verfasst: Mo 11.05.09 14:57
Jakob_Ullmann hat folgendes geschrieben : | Thorsten83 hat folgendes geschrieben : | | Was macht dein Parser bei nicht assoziativen Operationen? |
Wie meinst du das? |
Naja, die Frage ist halt, wie z.B. 3 - 2 - 1 ausgewertet wird, da ja (3 - 2) - 1 != 3 - (2 - 1)
Die Frage stellt sich ohne Klammerung in jeder Notation.
@Edi:
Magst du deinen Code als Code formatieren?
Das funktioniert so jetzt aber nur mit genau dem einen Term, oder?
Was du natürlich machen kannst ist folgendes:
Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9:
| char[] operators = {'^', '*', '/', '+', '-'); for(int i = 0 ; i<operators.length ; i++) { int index ; while((index = term.indexOf(operators[i]) )> 0) { // do something } } |
Wie man dann weitermacht ist halt Geschmackssache, ich hab mal was mit einer textuellen Ersetzung probiert, klappte ganz gut, eignet sich aber so, wie ich's jetzt habe, nur für infix-Operatoren... (Also z.B. sin(3) würde damit nicht funktionieren)
|
|
EdiZ84 
Hält's aus hier
Beiträge: 15
|
Verfasst: Mo 11.05.09 17:38
Hi Thorsten , danke dir nochmal, hab dein Code ausprobiert, und versuche den jetzt erstmal in meinen zu integrieren.
Zitat von Thorsten:
Magst du deinen Code als Code formatieren?
Was meinst du damit, ob es hier im Form als Code angezeigt werden soll?????
Zitat von Thorsten:
Das funktioniert so jetzt aber nur mit genau dem einen Term, oder?
Ja die Aufgabe funktioniert momentan nur mit einem Therm.
Muss ja noch einbisschen daran arbeiten.
Versuch das erstmal. Danke dir nochmal. Finde ich echt korrekt von dir. Du hast auf jeden Fall ein gut bei mir.
Ok,ciao
|
|
Thorsten83
      
Beiträge: 191
Erhaltene Danke: 1
|
Verfasst: Mo 11.05.09 17:56
Kein Ding, dafür ist das Forum ja da
Möchtest du meinen aktuellen Source haben? Der läuft halt ungefähr vollständig auf char-Arrays, und nimmt da Ersetzungen vor...
Er kann bis jetzt: ^, *, /, +, - (  ), und sowohl mit als auch ohne Klammern eine korrekte Auswertung vornehmen (Klammern vor ^ vor Punktrechnung vor Strichrechnung, Klammern sind nur noch für die Prioritäten notwendig, 1 + 2 + 3 würde er also auch verstehen...)
|
|
EdiZ84 
Hält's aus hier
Beiträge: 15
|
Verfasst: Di 12.05.09 14:06
Hi Thorsten
also dazu sage ich nie nein. Klar, wenn du in mir schicken könntest. Falls du das auf meine E-Mail machen möchtest, kannst du das auf die Adresse E.Zweer@gmx.de schicke.
Danke dir für das Angebot.
Ciao
|
|
Kha
      
Beiträge: 3803
Erhaltene Danke: 176
Arch Linux
Python, C, C++ (vim)
|
Verfasst: Di 12.05.09 19:30
Thorsten83 hat folgendes geschrieben : | | Möchtest du meinen aktuellen Source haben? Der läuft halt ungefähr vollständig auf char-Arrays, und nimmt da Ersetzungen vor... |
Wäre schön, wenn du zumindest die finale Version dann auch fürs restliche Forum zugänglich machen würdest  .
Und ich weiß jetzt nicht, welchen Algorithmus du genau verwendest, aber ich würde doch einen theoretisch fundierten empfehlen. Natürlich ist String-Manipulation am Anfang eingängiger als irgendwelche komischen Grammatiken, aber das Ergebnis lässt imo an Eleganz und auch Klarheit nichts zu wünschen übrig. Oder hat hier irgendjemanden etwas gegen meinen kleinen Recursive Descent Parser  ?
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93:
| static void Main(string[] args) { Console.WriteLine(Parse("1+2*(5*4+2)*3")); }
static void Read(ref string s, char c) { if (s[0] != c) throw new Exception(); s = s.Substring(1); }
static bool Eof(string s) { return s == ""; }
static bool TryRead(ref string s, char c) { bool success = !Eof(s) && s[0] == c; if (success) s = s.Substring(1); return success; }
static int Number(ref string s) { int num = int.Parse(new string(Enumerable.TakeWhile(s, Char.IsDigit).ToArray())); s = new string(Enumerable.SkipWhile(s, Char.IsDigit).ToArray()); return num;
Match match = Regex.Match(s, @"^(\d+)(.*$)"); s = match.Groups[2].Value; return int.Parse(match.Groups[1].Value); }
static int Factor(ref string s) { if (TryRead(ref s, '(')) { int num = Expression(ref s); Read(ref s, ')'); return num; } else return Number(ref s); }
static int Term(ref string s) { int num = Factor(ref s); while (true) if (TryRead(ref s, '*')) num = num * Factor(ref s); else if (TryRead(ref s, '/')) num = num / Factor(ref s); else return num; }
static int Expression(ref string s) { int num = Term(ref s); while (true) if (TryRead(ref s, '+')) num = num + Term(ref s); else if (TryRead(ref s, '-')) num = num - Term(ref s); else return num; }
static int Parse(string expression) { int result = Expression(ref expression); if (!Eof(expression)) throw new ArgumentException("expression");
return result; } |
Natürlich nicht ganz vollständig, aber durch die klare Struktur ist das Erweitern kein großes Problem.
_________________ >λ=
|
|
Thorsten83
      
Beiträge: 191
Erhaltene Danke: 1
|
Verfasst: Di 12.05.09 22:59
Kha hat folgendes geschrieben : | Thorsten83 hat folgendes geschrieben : | | Möchtest du meinen aktuellen Source haben? Der läuft halt ungefähr vollständig auf char-Arrays, und nimmt da Ersetzungen vor... | Wäre schön, wenn du zumindest die finale Version dann auch fürs restliche Forum zugänglich machen würdest .
Und ich weiß jetzt nicht, welchen Algorithmus du genau verwendest, aber ich würde doch einen theoretisch fundierten empfehlen. Natürlich ist String-Manipulation am Anfang eingängiger als irgendwelche komischen Grammatiken, aber das Ergebnis lässt imo an Eleganz und auch Klarheit nichts zu wünschen übrig. |
Ich kann ihn auch direkt hier posten, aber bitte nicht hauen
Die Idee ist folgende:
Das Programm liest einen Term ein, z.B. "3 + (5*4 + 1)^2"
Im ersten Schritt wird ein ')' gesucht, dann das davor liegende '('.
Der dazwischen liegende Term wird ausgewertet, und das Ergebnis in den Ursprungsterm eingesetzt, heraus kommt also
3 + 21^2
Es wird nochmal ein ')' gesucht, aber keines gefunden, also wird der Endterm ausgewertet.
Wir haben diese Art der textuellen Ersetzung im ersten Semester mal als applikative Auswertung behandelt, angeblich sollen manche Scheme-Interpreter so arbeiten, allerdings dann in polnischer Notation (womit sich das Problem der Prioritäten der Operatoren halt erledigt...)
Allerdings hab ich halt auf die schnelle ein paar Funktionen zusammengeklatscht, die z.B. den "Startindex" einer Zahl in einem char-Array zurückgeben, hätte man evtl. auch schöner machen können.
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199: 200: 201: 202: 203: 204: 205: 206: 207: 208: 209: 210: 211: 212: 213: 214: 215: 216: 217: 218: 219: 220:
| using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Media; namespace Parser { class Program { char[] operators = { '^', '*', '/', '+', '-' }; private bool check(char[] chars) { int tmp = 0; int i; for (i = 0; i < chars.Length; i++) { if (chars[i] == '(') { tmp++; } else if (chars[i] == ')') { tmp--; } if (tmp < 0) { return false; } } return tmp == 0; } private int parse(String str) { int tmp = 0; int start, end; char[] chars; str.Replace(" ", ""); chars = str.ToCharArray(); if (!check(chars)) { Console.WriteLine("Formula isn't valid..."); return 0; } while((end = getFirst(chars, ')')) > 0) { start = getClosest(chars, end, '('); tmp = calc(chars, start + 1, end - 1); chars = replace(chars, start, end, tmp.ToString().ToCharArray()); } return calc(chars, 0, chars.Length-1); } private int calc(char[] chars, int start, int end) { char[] copy = new char[end - start + 1]; int i; int index = 0; int pos; bool replaced; int op1; int op2; int result; for (i = start ; i <= end ; i++) { copy[index++] = chars[i]; } while (containsoperators(copy)) { replaced = false; for (i = 0; i < operators.Length && !replaced ; i++) { if ((pos = getFirst(copy, operators[i])) > 0) { replaced = true; op1 = getNumberBefore(copy, pos); op2 = getNumberAfter(copy, pos); start = getFirstNumeric(copy, pos - 1, -1); end = getFirstNumeric(copy, pos + 1, 1); result = evaluate(op1, op2, copy[pos]); copy = replace(copy, start, end, result.ToString().ToCharArray()); } } } return readNumber(copy, 0); } private int evaluate(int op1, int op2, char op) { switch (op) { case '+': return op1 + op2; case '-': return op1 - op2; case '*': return op1 * op2; case '/': return (op2 != 0 ? op1 / op2 : 0); case '^': return (int)Math.Pow((int)op1, (int)op2); default: return 0; } } private int getFirstNumeric(char[] chars, int start, int delta) { if (start < 0 || start >= chars.Length || !isANumber(chars[start])) { return start - delta; } return getFirstNumeric(chars, start + delta, delta); } private bool containsoperators(char[] chars) { for (int i = 0; i < chars.Length; i++) { if (!isANumber(chars[i])) { return true; } } return false; } private char[] replace(char[] chars, int start, int end, char[] replaceWidth) { char[] result = new char[chars.Length - (end - start + 1) + replaceWidth.Length]; int i; int index = 0; for (i = 0; i < start; i++) { result[index++] = chars[i]; } for (i = 0; i < replaceWidth.Length; i++) { result[index++] = replaceWidth[i]; } for (i = end + 1; i < chars.Length; i++) { result[index++] = chars[i]; } return result; } private int getNumberBefore(char[] chars, int pos) { int result = 0; int factor = 1; pos--; do{ result += (chars[pos] - '0')*factor; factor *= 10; pos--; }while(pos >= 0 && isANumber(chars[pos])); return result; } private int getNumberAfter(char[] chars, int pos) { int result = 0; pos++; do{ result *= 10; result += (int)chars[pos]-'0'; pos++; }while(pos < chars.Length && isANumber(chars[pos])); return result; } private int getFirst(char[] chars, char char2Find) { for (int i = 0; i < chars.Length; i++) { if (chars[i] == char2Find) { return i; } } return -1; } private int getClosest(char[] chars, int start, int char2Find) { for (int i = start; i >= 0; i--) { if (chars[i] == char2Find) { return i; } } return -1; } Boolean isANumber(char c) { return c >= '0' && c <= '9'; } int readNumber(char[] letters, int index) { int result = 0; do { result *= 10; result += letters[index] - '0'; index++; } while (index < letters.Length && isANumber(letters[index])); return result; } static void Main(string[] args) { Console.WriteLine("Formel eingeben..."); String str = Console.ReadLine(); char[] letters = str.ToCharArray(); Program p = new Program(); int result= 0; result = p.parse(str); Console.Write(result); Console.ReadLine(); } } } |
|
|
EdiZ84 
Hält's aus hier
Beiträge: 15
|
Verfasst: Mo 15.06.09 13:57
Hallo Leute. Wie gehts euch allen.
Ich habe jetzt endlich mal mein Parser erstellt. Jetzt ist meine große Frage wie ich die Punkt vor Strich-Rechnung einbaue. Und zwar habe ich gehört das man das Array mit den gesplitteten Teilen einfach nur sortieren müsste. Aber für andere Vorschläge bin ich immer offen. Könntet ihr mir dabei irgendwie helfen??
Danke euch im Voraus
Hier mein Parser:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199: 200: 201: 202: 203: 204: 205: 206: 207: 208: 209: 210: 211: 212: 213: 214: 215: 216: 217: 218: 219: 220: 221: 222: 223: 224: 225: 226: 227: 228: 229: 230: 231: 232: 233: 234: 235: 236: 237: 238: 239: 240: 241: 242: 243: 244: 245: 246: 247:
| using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms;
namespace Parser { public partial class Form1 : Form { public Form1() { InitializeComponent(); }
private void txt_eingabe_TextChanged(object sender, EventArgs e) {
try { var formula = txt_eingabe.Text.Trim(); if (checkBrackets(formula)) { lbl_result.Text = calcFormulaWithBrackets(formula).ToString(); } else { lbl_result.Text = "Klammer setzung fehlerhaft"; }
} catch (Exception ex) { lbl_result.Text = ex.Message; } }
private bool checkBrackets(string formula) { int bracketCount = 0;
foreach (var character in formula) { if (character == '(') { bracketCount++; } else if (character == ')') { bracketCount--; } } return (bracketCount == 0); }
private int calcFormulaWithBrackets(string formula) { int maxDepth = 0;
foreach (var character in formula) { if (character == '(') { maxDepth++; } } for (int formulaDepth = maxDepth; formulaDepth > 0; formulaDepth--) { bool subFormulaReplaced = false;
do { subFormulaReplaced = false;
int actDepth = 0;
for (int charIndex = 0; charIndex < formula.Length; charIndex++) { if (formula[charIndex] == '(') { actDepth++; } else if (formula[charIndex] == ')') { actDepth--; } if (actDepth == formulaDepth) { int closingBracketIndex = formula.IndexOf(')', charIndex);
var subFormula = formula.Substring(charIndex + 1, (closingBracketIndex - charIndex) - 1);
var subFormulaResult = calcFormulaWithOutBrackets(subFormula);
formula = formula.Replace(string.Format("({0})", subFormula), subFormulaResult.ToString());
subFormulaReplaced = true; break; } } } while (subFormulaReplaced); }
return calcFormulaWithOutBrackets(formula); }
private int calcFormulaWithOutBrackets(string formula) { var formulaParts = splitFormula(formula);
int result = int.Parse(formulaParts[0]);
for (int index = 1; index < formulaParts.Length; index += 2) { int partValue = int.Parse(formulaParts[index + 1]);
switch (formulaParts[index]) { case "+": result = (result + partValue); break; case "-": result = (result - partValue); break; case "*": result = (result * partValue); break; case "/": result = (result / partValue); break; default: throw new Exception(string.Format("Unbekannter Operator ({0})", formulaParts[index])); } }
return result; }
private string[] splitFormula(string formula) { List<string> result = new List<string>();
var operators = new char[] { '+', '*', '-', '/', ':' };
int lastOperatorIndex = 0;
string part;
for (int index = 0; index < formula.Length; index++) { if (operators.Contains(formula[index])) { part = formula.Substring(lastOperatorIndex, (index - lastOperatorIndex)).Trim();
validateFormulaPart(ref part);
result.Add(part); result.Add(formula[index].ToString());
lastOperatorIndex = (index + 1); } }
part = formula.Substring(lastOperatorIndex).Trim();
validateFormulaPart(ref part);
result.Add(part);
if ((result.Count % 2) == 0) { if (result[0].Length == 1 && operators.Contains(result[0][0])) { result.Insert(0, "0"); } else { throw new Exception("Anzahl der Operatoren passt nicht"); } }
return result.ToArray(); }
private void validateFormulaPart(ref string formulaPart) { int intValue;
formulaPart = formulaPart.Trim();
if (formulaPart.Length == 0) { formulaPart = "0"; } }
} } |
Moderiert von Kha: C#-Tags hinzugefügt
|
|
|