Java String mit einer Rechnung in einen in umwandeln

SparkMonkay

Commander
Registriert
Feb. 2013
Beiträge
2.337
Moinsen,
ich sitze mehrere Stunden davor dieses Problem zu lösen, kann sein, dass ich nicht drauf komme weil ich zu blöd bin oder was auch immer.

Ich habe einen String der eine Rechnung enthält und ich möchte das ausrechnen, jedoch soll Punk vor Strich Rechnung beachtet werden.
Nehmen wir das Beispiel
Code:
String a="123+124-1*2";
Mein Weg ist mir so kompliziert, dass ich kurz vor dem Zusammenrechnen mit Beachtung von Punkt und Strich Rechnung den Überblick verliere und absolut keinen Plan habe was ich getan habe...
Mit diesen vielen Multidimensionalen Arrays wird mir das etwas zu bunt um ehrlich zu sein.

Kurz gesagt.
Wie kann ich den String in einen Code Abschnitt umwandeln oder eine Methode verwenden, die diesen String zu
Code:
int rechnung=123+124-1*2;
macht bzw. etwas äquivalentes realisieren kann.

Danke für hilfreiche Antworten.
 
Ok, dann habe ich falsch gegoogelt.
@dozer
Ok, JS werde ich mal schauen wie ich es in meine FX Applikation einbinde

@KeepXtreme
Das mit dem Interpreter sieht sehr einfach aus und nicht so kompliziert wie bei mir.
 
beachte bitte, dass das eine Sicherheitslücke ist, wenn du dem String nicht 1000% vertraust!
 
Hallo,

wären Funktionen wie

  • 1 int indexOf(String str)
    indexOf(String str, int fromIndex)
  • 2 int lastIndexOf(String str)
    lastIndexOf(String str, int fromIndex)

Returns the index within this string of the first (or last) occurrence of the specified substring [searching forward (or backward) starting at the specified index].

eine Lösung? Man könnte wie folgt vorgehen:
Der String
Code:
"123+124-1*2"
wird im ersten Schritt nach den Rechenzeichen durchsucht:

Code:
int index = a.indexOf("*");
while (index >= 0) {
    System.out.println(index);
    index = word.indexOf(guess, index + 1);
}
wobei
Code:
String guess = "*";
dein aktuelles Rechenzeichen ist.
Liefert alle Positionen an denen ein "*" im String a steht. Selbes Vorgehen für die anderen Rechenzeichen. Am Ende weren die Positionen in ein Array geschrieben und es ergibt sich eine Indexfolge aller Rechenzeichen im String - man weiß also auch wo eine Zahl "anfängt" und wieder "aufhört".
Nun werden die Zahlen rechts (index+1) und links (index-1) von Rechenzeichen wie "*" oder "/" verrechnet und ein neuer String mit den Teilergebnissen zusammen gesetzt. Nun wird mit den Rechenzeichen "+" und "-" fortgefahren.

Du wirst etwas mit den Indizes beim hin- und herschubsen aufpassen müssen wenn die Teile des Strings mit "+" oder "-" kopierst und die Teilergebnisse der Multiplikation und Division einfügst.
 
@KeppExtreme
ich schreibe einen kleinen Taschenrechner, warum sollte ich meinem Taschenrechner nicht vertrauen können?

@Nuub
so in der Art habe ich es auch gemacht.

Gut ich werde es nun mit der BeanShell versuchen und entsprechend vorher einige Sicherheitsmechanismen einbauen, sodass der String sicher ist, also nur Zahlen enthält.
 
Die beste Lösung ist, eine fertige Komponente einzubinden. Wie heißt es so schön: Erfinde das Rad nicht neu.

Wenn das - aus welchen Gründen auch immer - nicht in Frage kommt oder gewünscht ist, dann ist die beste Lösung, einen Parser zu bauen. Im ersten Schritt würde dabei ein Lexer den String in eine Folge von Tokens umwandeln, die die einzelnen Elemente des Ausdrucks repräsentieren (also die Operatoren [+, *, ...] und die Zahlen). Anschließend baut man daraus einen Syntaxbaum, anhand dessen der Ausdruck dann ausgewertet werden kann. Dafür ist ein bisschen Theoriewissen notwendig, im Netz sollte es aber entsprechende Informationen geben.
 
Solane du nur die Grundrechenarten betrachtest und es keine Klammern gibt (was aber auch möglich wäre), kannst du einfach einen Vereinfachungsprozess laufen lassen.
Für eine beliebige Formel, z.B. 3+4/8*2 suchst du nach einzelnen Multiplikationen/Divisionen und führst diese links-assoziativ aus, du findest also zuerst die Division 4/8 was 0,5 entspricht und ersetzt dann diese Division in der Formel durch 0,5: 3+0,5*2
Und diesen Schritt mit dem Suchen der Multiplikationen/Divisionen machst du eben solange, bis keine mehr vorhanden sind, du erhälst also 3+1 welches du dann einfach ausrechnen kannst.

Hast du nach Klammern in deinen Formeln musst du eben erst nach Formeln in der Klammer suchen, diese geklammerte Formel ausführen (Rekursion ;) denn die kann ja auch wieder Klammern enthalten) und vereinfachst somit eben schrittweise die Formel:

also würde deine Funktion in etwa so aussehen:
Code:
myFunction(String formel):
  solange wie Formeln in der Klammer sind
    1. suche geklammerte Formel (erste öffnende Klammer bis Anzahl gefundene schließende Klammern gleich gezählter öffnender Klammern)
    2. lasse geklammerte Formel mit myFunction() ausrechnen
    3. ersetze geklammerte Formel mit zuvor errechnetem Ergebnis

  solange Multiplikationen/Divisonen in der Formel sind:
    habe ich oben beschrieben

  nun nur noch die übrigen Additionen und Subtraktionen ausführen.
 
Man könnte sich auch einfach eine Grammatik für die Ausdrücke ausdenken und einen LL-Parser schreiben, der das ganze auswertet.

Code:
Sum -> Product '+' Sum | Product
Product -> Factor * Product | Factor
Factor -> Zahl | '(' Sum ')'

Will heißen: Eine Summe ist entweder ein Produkt gefolgt von einem +-Zeichen und einer weiteren Summe oder ein Produkt.
Ein Produkt ist entweder ein Faktor gefolgt von einem *-Zeichen und einem weiteren Produkt oder ein Faktor.
Ein Faktor ist entweder eine Zahl oder eine geklammerte Summe.

Man müsste sich jetzt für jede dieser 6 Regeln und für jede der drei "Sorten" je eine Funktion schreiben, die einerseits testet, ob der String an der folgenden Stelle der Regel entspricht oder nicht, und andererseits auch den überprüften Teil auswertet. Mal etwas Pseudocode:

Code:
bool Symbol(String s, Int& pos, String sym):  // pos als Referenz
  if s.subString(pos, sym.length) == sym:
    pos += sym.length
    return true
  else
    return false

// ...

Maybe<Int> Factor(String s, Int& pos):
  // ...


Maybe<Int> Product_Rule1(String s, Int& pos):
  Int _pos = pos                        // Temporäre Positionsvariable, um die andere
                                        // im Falle eines Fehlers nicht zu überschreiben

  Maybe<Int> factor1 = Factor(s, _pos)  // Ersten Faktor auswerten
  if factor1 == Nothing:                // Aufgeben bei Fehler
    return Nothing

  if !Symbol(s, _pos, "*"):             // Aufgeben, falls nächstes Zeichen kein '*'
    return Nothing

  Maybe<Int> factor2 = Factor(s, _pos)  // Zweiten Faktor auswerten
  if factor2 == Nothing:                // Aufgeben bei Fehler
    return Nothing

  pos = _pos                            // Erfolg - aktuelle Position zurückschreiben
  return factor1.value * factor2.value  // und Produkt zurückgeben


Maybe<Int> Product_Rule2(String s, Int& pos):
  return Factor(s, pos)


Maybe<Int> Product(String s, Int& pos):
  Maybe<Int> result
  if (result = Product_Rule1(s, pos)) != Nothing: return result  // Erste Regel prüfen
  if (result = Product_Rule2(s, pos)) != Nothing: return result  // Zweite Regel prüfen (Reihenfolge wichtig)
  return Nothing                                                 // Aufgeben bei Fehler

// ...

Maybe ist in dem Fall einfach ein Objekt, das entweder nicht definiert ist (Nothing) oder einen Wert speichert. Könnte man in Java ggf. mit Integer realisieren und als Nothing eben null zurückwerfen.

Das hat nebenbei den Vorteil, dass
a) man sich den Umweg über den Syntaxbaum spart, weil der String einfach on the fly ausgewertet wird
b) sich der Ansatz leicht erweitern und sogar so weit generalisieren lässt, dass man nur noch die Grammatik und die Funktionen angeben muss - sofern einen die Programmiersprache da nicht behindert.
 
Zuletzt bearbeitet:
Und morgen lernen wir dann den Umgang mit negativen Zahlen. (Um schon mal die nächste Frage vorwegzunehmen.)
 
Bin Grad nicht am PC und entwickel etwas auf dem Papier, denke ich habe es bald und das nette dabei ist, dass ich diese Methode einfach erweitern kann.

//EDIT:
Der Code muss nun nur noch in dem Sinne diesen Fehler ausgemerzt bekommen dann müsst er laufen.
Code:
Exception in thread "main" java.lang.NumberFormatException: For input string: "012+4"
	at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:2043)
	at sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110)
	at java.lang.Double.parseDouble(Double.java:538)
	at test.Test.preCalculate(Test.java:59)
	at test.Test.main(Test.java:88)
Java Result: 1

@r0b0t
Nö, das müsste das Ding auch können, das waren jeweils nur 1 if Befehl....
 
Zuletzt bearbeitet:
Soweit funktioniert das Teil.
Es fehlen noch 2 Sachen.
Es soll in folgender Reihenfolge die Rechenzeichen arbgearbeitet werden.
+ --> / --> + --> -
Dann noch eine Schleife die prüft wie oft ich das mit dem jeweiligen Zeichen machen muss.

Code:
public static String preCalculate(String a, char c)
  {
      char array[]=a.toCharArray();
      if(array[0]=='*' || array[0]=='/') return "ERROR";
      
      int count=0;
        //Das erste vorkommende Rechenzeichen finden das ich haben will
        while(true)
        {
            count++;
                
                 if(array[count]==c){ break; }
            else if(count==array.length-1){ return a; }    
        }
       
        int countL=count;
        int countR=count;
        
        //Feststellen ob es ein negatives Vorzeichen gibt, wenn ja wird dieses Element mit hinein geschmuggelt.
        if(array[count+1]=='-')countR++;
        //Bis zum nächsten nachfolgenden Rechenzeichen gehen oder bis zum Ende
        while(true)
        {
            if(countR==array.length-1 || array[countR+1]=='+'|| array[countR+1]=='-' || array[countR+1]=='/' || array[countR+1]=='*'){ break; }
            countR++;
        }
        
        //Bis zum letzten vorherigen Rechenzeichen gehen oder bis zum 0. Element oder auch zum Vorzeichen
        while(true)
        {
            if(countL==0 || array[countL-1]=='+'|| array[countL-1]=='-' || array[countL-1]=='/' || array[countL-1]=='*')break;
            countL--;
        }
        //Klären ob ein '-' ein Vorzeichen oder ein Rechenzeichen ist.
        if(countL>0) if(array[countL]=='-' && array[countL-1]=='+'|| array[countL-1]=='-' || array[countL-1]=='/' || array[countL-1]=='*') countL--;
        
        //einmal countL und countR woanders noch abspeichern
        int countL2=countL, countR2=countR;
        
        
        
        //Die Rechnung
        //char-->String-->double-->String
        String left="";      String right="";      String result="";
        double leftDouble;   double rightDouble;   double resultDouble=0;


        while(countL <count ){left=left+array[countL];    countL++;}
        countL=count+1;
        while(countL<=countR){right=right+array[countL]; countL++; }

        leftDouble =Double.parseDouble(left);
        rightDouble=Double.parseDouble(right);

        System.out.println("left:"+left+"\nright:"+right);
        //Ausrechnen der beiden Zahlen
        switch(c)
        {
            case '*': resultDouble=leftDouble*rightDouble; break;
            case '/': resultDouble=leftDouble/rightDouble; break;
            case '+': resultDouble=leftDouble+rightDouble; break;
            case '-': resultDouble=leftDouble-rightDouble; break;
        }

        result=String.valueOf(resultDouble);
        

      return result;
  }

Muss auch noch verschönert werden ;)
 
Zurück
Oben