Frage von Dieter987, 70

Eigenen Stringparser schreiben in Java?

Guten Tag ich versuche gerade einen Taschenrechner zu erstellen.

ActionListener al =new ActionListener(){
    public void actionPerformed(ActionEvent e){

         String text = ((JButton)e.getSource()).getText();
         field.setText(field.getText()+text);
         
         
         if(e.getSource() == sum){
             field.setText(field.getText());
         }
         else if(e.getSource() == sub){
             field.setText(field.getText());
         }
         else if (e.getSource() == mult){
             field.setText(field.getText());
         }
         else if (e.getSource() == div){
             field.setText(field.getText());
         }
         
         erg.setText(field.getText());
     }
    
};
ActionListener al2=new ActionListener(){
    public void actionPerformed(ActionEvent f){
        field.setText("");
    }
};

Mir wurde der Rat gegeben einen eigenen Stringparser zu schreiben. Habe aber keine Ahnung wie.

Gibt es vielleicht noch eine andere Möglichkeit mit dem String zu rechnen? Das Problem sind ja die Rechenzeichen. Wie könnte man das lösen?

Vielen Dank im Voraus

Hilfreichste Antwort - ausgezeichnet vom Fragesteller
von androhecker, 36

Wenn du keine der unteren Methoden benutzen willst dann empfehle ich dir die andere Antwort.
1. Die JVM hat eine JavaScript Engine wodurch man auch einen String evaluator hat, Vorteil ist dass alles schon integriert ist und man nichts extra in die Jar tun muss
2. Es gibt im Internet genug String Evaluator bzw Parser wie zB EvalEx

Kommentar von Dieter987 ,

okay vielen Dank für ihre Antwort

habe folgendes Problem:

public class Test

{
public static void main(String[] args) {
String expression = "4 * 5";

// Get JavaScript engine
ScriptEngine engine = new ScriptEngineManager().getEngineByExtension("js");

try {
// Evaluate the expression
Object result = engine.eval(expression);

System.out.println(expression + " = " + result);
}
catch (ScriptException e) {
// Something went wrong
e.printStackTrace();
}
}
}

funktioniert super.

Nun hab ich es bei mir eingefügt:

 ActionListener al =new ActionListener(){

public void actionPerformed(ActionEvent e){

String text = ((JButton)e.getSource()).getText();
field.setText(field.getText()+text);

if(e.getSource() == sum){
field.setText(field.getText());
}
else if(e.getSource() == sub){
field.setText(field.getText());
}
else if (e.getSource() == mult){
field.setText(field.getText());
}
else if (e.getSource() == div){
field.setText(field.getText());
}
ScriptEngine engine = new ScriptEngineManager().getEngineByExtension("js");
try {
// Evaluate the expression
Object result = engine.eval(text);

System.out.print(result);
}
catch (ScriptException h) {
// Something went wrong
h.printStackTrace();
}

erg.setText(field.getText());
}

};

Jetzt erscheinen allerlei Fehlermeldungen bei Eingabe eines Rechenoperators.

Kommentar von androhecker ,

Wahrscheinlich kannst du das Problem finden, wenn du einfach die Rechnung in den Log schreibst, wenn es keine richtige Rechnung ist, dann liegt der Fehler im restlichen Programm.

Kommentar von Dieter987 ,

Da steht was von eof. 

9javax.script.ScriptException: :1:1 Expected an operand but found eof
+
^ in at line number 1 at column number 1
at jdk.nashorn.api.scripting.NashornScriptEngine.throwAsScriptException(NashornScriptEngine.java:467)
at jdk.nashorn.api.scripting.NashornScriptEngine.compileImpl(NashornScriptEngine.java:534)
at jdk.nashorn.api.scripting.NashornScriptEngine.compileImpl(NashornScriptEngine.java:521)
at jdk.nashorn.api.scripting.NashornScriptEngine.evalImpl(NashornScriptEngine.java:399)
at jdk.nashorn.api.scripting.NashornScriptEngine.eval(NashornScriptEngine.java:155)
at javax.script.AbstractScriptEngine.eval(Unknown Source)
at CalcPanel$1.actionPerformed(CalcPanel.java:87)
at javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
at javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.setPressed(Unknown Source)
at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(Unknown Source)
at java.awt.Component.processMouseEvent(Unknown Source)
at javax.swing.JComponent.processMouseEvent(Unknown Source)
at java.awt.Component.processEvent(Unknown Source)
at java.awt.Container.processEvent(Unknown Source)
at java.awt.Component.dispatchEventImpl(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Window.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access$500(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue$4.run(Unknown Source)
at java.awt.EventQueue$4.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.run(Unknown Source)
Caused by: jdk.nashorn.internal.runtime.ParserException: :1:1 Expected an operand but found eof
+
^
at jdk.nashorn.internal.parser.AbstractParser.error(AbstractParser.java:292)
at jdk.nashorn.internal.parser.AbstractParser.error(AbstractParser.java:277)
at jdk.nashorn.internal.parser.Parser.unaryExpression(Parser.java:3182)
at jdk.nashorn.internal.parser.Parser.unaryExpression(Parser.java:3114)
at jdk.nashorn.internal.parser.Parser.expression(Parser.java:3282)
at jdk.nashorn.internal.parser.Parser.expressionStatement(Parser.java:1150)
at jdk.nashorn.internal.parser.Parser.statement(Parser.java:967)
at jdk.nashorn.internal.parser.Parser.sourceElements(Parser.java:773)
at jdk.nashorn.internal.parser.Parser.program(Parser.java:709)
at jdk.nashorn.internal.parser.Parser.parse(Parser.java:283)
at jdk.nashorn.internal.parser.Parser.parse(Parser.java:249)
at jdk.nashorn.internal.runtime.Context.compile(Context.java:1286)
at jdk.nashorn.internal.runtime.Context.compileScript(Context.java:1253)
at jdk.nashorn.internal.runtime.Context.compileScript(Context.java:625)
at jdk.nashorn.api.scripting.NashornScriptEngine.compileImpl(NashornScriptEngine.java:532)
... 41 more

Kommentar von androhecker ,

Wie schon gesagt, in der Rechnung ist ein Fehler, das liegt nicht an der Engine. Gib einfach die komplette Rechnung in die Konsole aus.

Kommentar von Dieter987 ,

Ich soll aber eine Taschenrechner GUI erstellen, da nützt es mir nichts wenn ich es in der Konsole ausgebe.

Kommentar von androhecker ,

In die Konsole damit du den Fehler in der Rechnung siehst, das muss ja nicht im späteren Programm sein.

Kommentar von Dieter987 ,

Wenn ich das ausführe blinken in der Konsole kurzzeitig Fehlermeldungen auf aber dann 4*5=20

public class Test

{
public static void main(String[] args) {
String expression = "4 * 5";

// Get JavaScript engine
ScriptEngine engine = new ScriptEngineManager().getEngineByExtension("js");

try {
// Evaluate the expression
Object result = engine.eval(expression);

System.out.println(expression + " = " + result);
}
catch (ScriptException e) {
// Something went wrong
e.printStackTrace();
}
}
}

Das habe ich dann auch im Taschenrechnerprogramm und sobald ich auf eine Rechenzeichen klicke werden mir in der Konsole zahlreiche Fehlermeldungen ausgegeben.

Kommentar von androhecker ,

Du sollst in deinem Taschenrechner vor dem try Block die gesamte Expression in die Konsole stehen, die oben genannten Fehler treten nur auf wenn die Rechnung inkorrekt ist, wie zB "*" oder "5*"

Kommentar von Dieter987 ,

okay jetzt hab ich es begriffen.

Eine Sache wäre aber noch. Ich habe ja ein Objekt result das Ergebnis kann ich in der Konsole sehen,aber um es in der GUI anzeigen zulassen brauch ich einen String. Wie kann ich das Objekt result in einen String konvertieren?

Kommentar von androhecker ,

Mit String.valueOf(result) oder mit result.toString()

Antwort
von MalNachgedacht, 9

Also zunächst einmal sind solche if then else Orgien 

 if(e.getSource() == sum){
field.setText(field.getText());
}
else if(e.getSource() == sub){
field.setText(field.getText());
}
else if (e.getSource() == mult){
field.setText(field.getText());
}
else if (e.getSource() == div){
field.setText(field.getText());
}

ein ziemlich sicherer Hinweis dafür, dass mit dem Programmdesign etwas nicht stimmt.

In deinem Fall dass Du verschiedene Buttons die unterschiedliche Dinge tun in einer einzigen Methode abzuarbeiten versuchst.

Stell Dir vor Du hättest gerne weitere Funktionen wie Sinus, Cosinus, Quadratwurzel usw - dann würde diese "Orgie" immer größer

Stattdessen wären wohl ein separater ActionListener für jede "Taste" deinen Taschenrechners die übersichtlichere Variante

Um die Berechnungen des Taschenrechners durchzuführen kann man natürlich eine Interpreter für eine entsprechende Scriptsprache einbinden und ausnutzen, dass solche Scriptsprachen üblicherweise mathematische Terme die als String vorliegen auswerten können.

Das ist zwar eine schnell implementierte Lösung - aber auch ziemlicher Overkill.

Bei einem Ausdruck wie    3 * ( 4 + 2 * 5)   

muß man eigentlich nur ermitteln welcher der Operatoren die niedrigste Priorität hat - also zuletzt berechnet wird.

Dazu braucht man einen simplen "Parser" der den String von links nach rechts durchließt und sich die Position des bislang gefundenen niedrigsten Operator sowie die "Klammerebene" merkt

Dann klappert man den String in einer Schleife von pos 0 bis length()-1 ab und prüft z.B. in einem Switch -Statement das aktuelle Zeichen:

Ist es (  erhöht man die Klammerebene um 1

Ist es ) erniedrigt man die Klammebene um 1 (und prüft ob sie kleiner als 0 ist was durch einen fehlerhafte Klammerung hinweis

Ist es  plus oder minus  errechnet man die Priorität des Operators

z.B. mit KlammerEbene*10+1   und prüft ob die kleiner minPriority ist - falls ja merkt man sich die aktuelle Position im String in minPos und setzt minPriority auf das neue Minimum

Ist es mal  oder geteilt  errechnet man die Priorität des Operators

z.B. mit KlammerEbene*10+2   und prüft ob die kleiner minPriority ist - falls ja merkt man sich die aktuelle Position im String in minPos und setzt minPriority auf das neue Minimum

Nachdem man nun bestimmt hat an welcher Stelle im String der Operator mit der niedrigsten Priorität steht teilt man den String an der Stelle in eine linke Hälfte (vor dem Operator) und eine rechte Hälfte (nach dem Operator) und ruft für beide Hälften rekursiv erneut die parser-Function auf die jeweils den entsprechenden numerischen Wert zurückliefert

Man hat also nun den numerischen Wert des Terms links vom Operator und den numerischen Wert des Terms rechts vom Operator und muß nun diese beiden Werte miteinander verrechnen - je nach Operator eben addieren, mulitplizieren, dividieren usw - und das Ergebnis als Funktionswert zurückliefern

Finden man im String gar keinen Operator sondern besteht der nur aus Ziffern kann man den String mit entsprechenden Standardfunktionen direkt in einen double-Wert umrechen und den dann als Ergebnis der parser-Function zurückliefern....

Wichtig ist eben der rekursive Ansatz der parser-Function wodurch dann beliebig lange Ausdrücke in der richtigen Reihenfolge ausgewertet werden.

Ist auf jedenfall vom Lerneffekt her besser als eine Script-Engine einzubinden - und braucht auch deutlich weniger Speicherplatz

Mit einem ähnlichen Parser kann man den eingegebenen Term auch  graphisch schön aufbereiten - als zweidimensionalen Formel mit Bruchstrich usw.

Antwort
von Korashen, 52

http://stackoverflow.com/questions/950409/how-to-parse-this-string-in-java

Keine passende Antwort gefunden?

Fragen Sie die Community