eval string Minuszeichen


#1
Guten Morgen,

ich habe einen "Rechen" String

z.B.

erg= "3 + 4 *5 - 6 +2 "

den kann ich berechnen mit
erg=eval(erg)

Bei der Berechnung gilt die Regel Punktrechnung vor Strichrechnung.
Ich möchte den String aber ohne diese Rechenvorschrift berechnen

Mein Ansatz war:

var erg=eval((string.slice(0,3)));
erg= eval(erg +string.slice(3,5));
erg=eval(erg +string.slice(5,7));

Das klappt nicht wenn ein Minus auftaucht.

Lösungsidee?
 

Technipion

Erfahrenes Mitglied
#3
Ich möchte den String aber ohne diese Rechenvorschrift berechnen
Wie genau meinst du das?
Meinst du quasi von vorne nach hinten:
Code:
  3 + 4 * 5 - 6 + 2
= 7     * 5 - 6 + 2
= 35        - 6 + 2
= 29            + 2
= 31
Ich verstehe zwar den Sinn davon nicht, aber okay :D
Ich würde dafür so vorgehen:


1) Finde den Index des zweiten Operators:

Code:
  3 + 4 * 5 - 6 + 2
        ^
        | den Index dieses Zeichens

2) Evaluiere den Teilstring bis zu diesem Index:

Code:
ergebnis = eval("3 + 4 "); // = 7

3) Baue einen neuen String aus dem Ergebnis von 2) und dem hinteren Teil deiner Eingabe:

Code:
neuer_string = ergebnis + "* 5 - 6 + 2";
               \  =7  /   \   Teil 2   /
4) Wiederhole 1). Wenn es nur noch einen Operator gibt, ruf ein letztes Mal eval() auf:

Code:
  7*5 - 6 + 2
      ^
      | hier wird als nächstes gesplittet

  35- 6 + 2
        ^
        | danach hier

  29+ 2 // nur noch ein Operator -> ruf ein letztes Mal eval() auf

  eval("29+ 2"); // = 31

Kann natürlich auch sein, dass ich das ganze irgendwie falsch verstanden habe...

Gruß Technipion
 

basti1012

Erfahrenes Mitglied
#4
vielleicht reicht es Klammern zu setzen?
Javascript:
eval("(3 + 4) * 5 - 6 + 2 ")
Funktioniert bei mir auch so ohne die Kommas oben( Name dafür entfallen )
Code:
 eval((3 + 4) * 5 - 6 + 2)
Aber wahrscheinlich ist der weg zu einfach.
Bei Php sagt man doch immer das eval böse ist . Ist das bei Javascript nicht das gleiche ?
Las doch eval weg, weil zb beim Alert wird die richtige Lösung ( 31 ) auch so angezeigt
Code:
 alert((3 + 4) * 5 - 6 + 2)
Also müsste das doch auch ohne eval gehen oder sehe ich das jetzt falsch ???
 

Technipion

Erfahrenes Mitglied
#5
Bei Php sagt man doch immer das eval böse ist . Ist das bei Javascript nicht das gleiche ?
Jein. Grundsätzlich stimmt es natürlich, dass man auf eval() (in egal welcher Sprache) verzichten sollte, falls das geht. Der Grund warum in PHP, aber auch in Python, Java, und den meisten anderen interpretierten Sprachen davon abgeraten wird ist, dass es prinzipiell einem Angreifer ermöglicht seinen Code in ein sonst (hoffentlich) gesichertes Computersystem einzuschleusen.

Wenn du z.B. in PHP sowas hier bringst eval($_POST["rechnung"]); kann ein Angreifer ja relativ schnell eigenen Code per POST an deinen Server senden, und seinen eigenen Code auf deinem System ausführen. Das ist schlecht.

Bei JavaScript könnte dir das im Prinzip auch passieren, aber ich gehe mal davon aus, dass @Jofre hier ein Programm für den Client entwirft, also Code der vom Server gedownloaded und dann im Browser ausgeführt wird. Da entfällt das Risiko im Pinzip, denn falls etwas schief geht ist es auf dem Rechner des Clients.

Man muss natürlich beachten wie genau das Resultat von eval(); hier gehandhabt wird. Es wäre z.B. eine sehr schlechte Idee den Ausdruck in JavaScript mit eval(); auszuwerten und dann in irgendeiner Form bei einem Datenbankzugriff zu verwenden...
Es gilt: Don't trust the user!

Fun Fact: Eigentlich bietet JavaScript richtig nette Spielereien mit eval(); (guckst du z.B. hier math.js | an extensive math library for JavaScript and Node.js), aber da Jofre gegen die gottgegebene Operator-Rangfolge verstoßen will, muss er sich da selbst etwas einfallen lassen.

Fun Fact 2: Mal von den Sicherheitslücken abgesehen kann bei eval();-Befehlen noch viel mehr schief gehen. Man stelle sich vor, was wohl bei einem kleinen Tippfehler mit eval("9722349**662584155"); passiert... (Tipp: Die Zahl braucht mehr als 15 GB RAM ;))

Gruß Technipion
 

Quaese

Moderator
Moderator
#6
Hi,

oder als Einzeiler-Spielerei mit regulären Ausdrücken und Array-Funktion:
Javascript:
"3 + 4 *5 - 6 +2 ".match(/(\d+)|([\+\*\-\/\:])/g).reduce((accu, curr) => isNaN(curr) ? (accu + (curr === ':' ? '/' : curr)) : eval(accu + curr));
Ciao
Quaese
 

ComFreek

Mod | @comfreek
Moderator
#7
Bei JavaScript könnte dir das im Prinzip auch passieren, aber ich gehe mal davon aus, dass @Jofre hier ein Programm für den Client entwirft, also Code der vom Server gedownloaded und dann im Browser ausgeführt wird. Da entfällt das Risiko im Pinzip, denn falls etwas schief geht ist es auf dem Rechner des Clients.
Das stimmt so nicht ganz. Zum Beispiel können Nutzerdaten und die aktuelle Session gestohlen werden. Das ist im Prinzip dasselbe Problem wie bei XSS.

Fun Fact: Eigentlich bietet JavaScript richtig nette Spielereien mit eval(); (guckst du z.B. hier math.js | an extensive math library for JavaScript and Node.js), aber da Jofre gegen die gottgegebene Operator-Rangfolge verstoßen will, muss er sich da selbst etwas einfallen lassen.
Die verlinkte Bibliothek hat eigentlich nichts mit dem JS eval zu tun, außer dass sie ihre "parse & evaluate" Funktion auch eval genannt hat. Deren Funktion parst den mathematischen Ausdruck aber wirklich als solchen und nicht als JS.
Möglicherweise könnte man aber math.js so erweitern, indem man neue Operatoren definiert, die andere Precedence-Regeln haben.

Fun Fact 2: Mal von den Sicherheitslücken abgesehen kann bei eval();-Befehlen noch viel mehr schief gehen. Man stelle sich vor, was wohl bei einem kleinen Tippfehler mit eval("9722349**662584155"); passiert... (Tipp: Die Zahl braucht mehr als 15 GB RAM ;))
Nachdem JS keine built-in BigNum Library benutzt, sondern 64-Bit Double Zahlen, ist das Ergebnis hier dank Overflow +Infinity und verbraucht nicht mehr CPU-Zyklen oder Speicher als eine Double-Multiplikation normalerweise im Worst Case verbraucht ;)
 

Technipion

Erfahrenes Mitglied
#8
Nachdem JS keine built-in BigNum Library benutzt, sondern 64-Bit Double Zahlen, ist das Ergebnis hier dank Overflow +Infinity und verbraucht nicht mehr CPU-Zyklen oder Speicher als eine Double-Multiplikation normalerweise im Worst Case verbraucht ;)
Hast mich erwischt, ich war aus irgendeinem Grund gerade bei Python :LOL:

Die verlinkte Bibliothek hat eigentlich nichts mit dem JS eval zu tun, außer dass sie ihre "parse & evaluate" Funktion auch eval genannt hat.
Den Link hatte ich nur gepostet, weil sich Jofres Frage sehr nach einem starken Bezug zur Mathematik angehört hat. War also nur ins Blaue geraten.

@Jofre: @ComFreek hat da völlig Recht, du machst dich mit eval() für XSS (Cross-Site-Scripting) und ähnliche Attacken verwundbar. Im Grunde wäre es das beste, komplett auf eval() zu verzichten.
Am besten schreibst du uns mal, was genau du vor hast, dann basteln wir etwas zusammen.

Gruß Technipion
 
#9
Hast mich erwischt, ich war aus irgendeinem Grund gerade bei Python :LOL:


Den Link hatte ich nur gepostet, weil sich Jofres Frage sehr nach einem starken Bezug zur Mathematik angehört hat. War also nur ins Blaue geraten.

@Jofre: @ComFreek hat da völlig Recht, du machst dich mit eval() für XSS (Cross-Site-Scripting) und ähnliche Attacken verwundbar. Im Grunde wäre es das beste, komplett auf eval() zu verzichten.
Am besten schreibst du uns mal, was genau du vor hast, dann basteln wir etwas zusammen.

Gruß Technipion
 
#10
Zum Hintergrund

Ich möchte Kettenrechnzungen durchführen. Dafür erzeuge ich strings wie im obigen Beispiel. Die strings nuzen + - * / .

Wenn ich eine Ketten rechnung durchführe dann rechne ich stur von links nach rechts.
Der Ansatz von Quaese funktioniert. ich möchte aber nicht gerne etwas nutzen, das ich nicht verstehe.

Wahrscheinlich bleibt mir nur der Ansatz(Basti= mit dem Klammern.
Das wird aber eine grosse Fummelei . Der String könnte auch sein:

3-5*7 *2 + 4*-5 +2

Vielleicht ist es der einfachste und transparenste Weg jede Rechenoperation zu klammern.

(((((3-5)*2)+4)-5)+2)
 

Sempervivum

Erfahrenes Mitglied
#11
Dafür erzeuge ich strings wie im obigen Beispiel.
Wenn ich das lese, vermute ich dass dir die Zahlen und die Operatoren am Anfang getrennt und die Zahlen in numerischer Form bereit stehen. Dann sollte es kein Problem sein, die Berechnung ohne den Umweg über den String und eval in einer Schleife auszuprogrammieren?
 

ComFreek

Mod | @comfreek
Moderator
#12
Genau, @Sempervivum hat Recht! Der Algorithmus muss nur über 3 Zustände Bescheid wissen:
  1. Erster Operand wird erwartet (eine Zahl)
  2. Operator wird erwartet (+, -, *, /)
  3. Zweiter Operand wird erwartet (eine Zahl)
In Zustand 1 liest du solange, bis das nächste Zeichen keine Ziffer mehr ist und speicherst dann den gelesenen Operanden als Integer (siehe parseInt) irgendwo. In Zustand 2 liest du nur ein einzelnes Zeichen und speicherst, welche Operation du durchführen möchtest. Das muss im Prinzip in eine Variable gespeichert werden, dessen Typ vierwertig ist, weil du +, -, * und / hast. Du könntest da z. B. einfach Strings verwenden: "operator = '*'". (Für Fortgeschrittene: speichere es als Funktion.) In Zustand 3 machst du dasselbe wie in Zustand 1, aber nach dem Einlesen führst du nun die Operation durch und gehst danach wieder in Zustand 1.

Ich hoffe du kannst erkennen, dass das zu einem größeren Switch-Case oder If-Else ausarten wird ;)
 

Quaese

Moderator
Moderator
#13
Hi,

letztendlich bediene ich mich genau den Zuständen, die @ComFreek beschrieben hat. Ich handel diese lediglich in einem ternären Operator ab. Weiterhin nutze ich aus, dass eine einzelne Zahl ebenfalls mit eval ausgewertet werden kann. Die Reduktion auf einen einzelnen Wert (hier der Ergebnisstring bzw. das Ergebnis) erfolgt über die Array-Methode reduce.

Vielleicht kannst du mit dem kommentierten Quellcode mehr anfangen:

Javascript:
"3 + 4 *5 - 6 +2 "
    // teilt den String mit Hilfe eines regulären Ausdrucks in ein Array,
    // dessen Komponenten zwischen Zahl und Operator wechseln: ["3", "+", "4", "*", "5", "-", "6", "+", "2"]
    .match(/(\d+)|([\+\*\-\/\:])/g)
    // reduce reduziert ein Array auf einen einzigen Wert (siehe: https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce)
    .reduce((accu, curr) => {
        return isNaN(curr)  // Handelt es sich beim aktuellen Element NICHT um eine Zahl
            ? (accu + (curr === ':' ? '/' : curr)) // keine Zahl => Operator an aktuellen String anhängen
            : eval(accu + curr)                    // aktuelle Zahl an String anhänen und auswerten (eval)
    });
Ciao
Quaese
 
#14
Hallo Sempervivum,

es ist so wie du den Ausgangszustand beschrieben hast. Ich würde gerne dazu lernen wie das so einfach erledigt wird. OHNE Schleife.

GuG

Joachim
 

Sempervivum

Erfahrenes Mitglied
#15
Dann beschreibe doch mal, in welcher Form die Zahlen und Operatoren vorliegen. Ich hatte vermutet, in einem oder zwei Arrays, aber wenn Du schreibst "ohne Schleife", dann in getrennten Variablen?
 

ComFreek

Mod | @comfreek
Moderator
#16
Weiterhin nutze ich aus, dass eine einzelne Zahl ebenfalls mit eval ausgewertet werden kann.
Aber darf ich fragen warum? :) Das ist unsicher*, langsam, macht den Code schwerer lesbar und ist semantisch inkorrekt.

*) Auch wenn du vorher mit regulären Ausdrücken zusicherst, dass es nur Zahlen sind, so kann ein verunglücktes Refactoring immer Bugs einführen, die doch dazu führen, dass andere Daten in das eval gelangen könnten.
 
#17
Ich erzeuge den String in einer Schleife.
In der generiere ich eine Abfolge per Zufall : Zahlt, Operand, Zahl, Operand, Zahl....

ich könnte auch zunächst ein Array mit Zahlen und ein Array mit Operanden erzeugen, und dann den String erstellen ich hätten dann so etwas wie
zahlen = [5,5,8,2,6,9,3]
operand=["+", "+", "**,"*","-"],.
wenn es mir dann hilft.
GzG
Joachim
 

Sempervivum

Erfahrenes Mitglied
#18
Wenn Du ohnehin die Parameter in einer Schleife erstellst, kannst Du auch gleich die Berechnungen durchführen, etwa so:
Code:
Ergebnis = Zufallszahl
Schleife
    Zufallszahl erzeugen
    Zufallsoperator erzeugen
    Ergebnis und aktuelle Zufallszahl entspr. akt. Operator verknüpfen