php string berechnen ohne eval()
lima-city → Forum → Programmiersprachen → PHP, MySQL & .htaccess
array
aussehen
baum
benutzen
break
code
funktion
http
null
paar
punkt
rechnung
statement
stehen
string
suche
switch
url
zahl
zeichen
-
Hi, ich bin Toben und Praktikant bei einer Webdesign-Firma, ich habe die Aufgabe bekommen
einen Taschenrechner nur mit php zu programmieren.
Das klingt auf den ersten Blick sehr einfach, ich soll allerdings ein paar Punkte einhalten:
- ich darf die Funktion eval() nicht benutzen (aus sicherheits gründen)
- die gesamte Rechnung muss in einem Textfeld eingegeben werden können
- ich muss den String mit Hilfe von regulären Ausdrücken zerlegen
- ich muss Punkt- vor Strichrechnung beachten
- einfache (+-*/) Rechenzeichen reichen
hier ein kleiner Überblick wie es aussehen soll und wie weit ich bin:
http://tfttestseite.lima-city.de/
(der link ist nur richtig, wenn das Thema noch aktuell ist)
So siehts bis jetzt aus:
<?php // so könnte eine gleichung aussehen $exp = '30+8*27-15/3'; // zerlegt den String in einzelteile (zahlen wie "30" werden zusammengefügt) preg_match_all('/(\d+)|([-\*\/{+])/',$exp,$zeichen); // Variablendeklaration (der übersicht halber) $op = null; $v1 = null; $v2 = null; $v3 = null; $op2= null; echo "<center>"; // alle Zeichen werden von vorn bis hinten durchgegangen foreach($zeichen[0] AS $cur) { switch($cur) {// ist der aktuelle string ein Rechenzeichen, wird $op überschrieben case "+": case "-": case "/": case "*": $op = $cur; break; default:// ist der string eine Zahl, wird ne Menge gerechnet if($v1 == null) { $v1 = $cur;// ist es die erste Zahl, wird sie auf $v1 gelegt } else {// ist es nicht die erste, wird sie stattdessen auf $v2 gelegt $v2 = $cur; // hier kommen ganz viele if´s, wegen punkt vor strich, ist allerdings sehr // fehlerhaft, steht z.B. "3*3*2" wird falsch gerechnet switch($op) { case "+": if($op2 == "+")$v1 = $v1 + $v3; if($op2 == "-")$v1 = $v1 - $v3; $v3 = $v2;$op2 = "+";break; case "-": if($op2 == "+")$v1 = $v1 + $v3; if($op2 == "-")$v1 = $v1 - $v3; $v3 = $v2;$op2 = "-";break; case "*": if($op2 == "+")$v1 = $v1 + ($v3 * $v2);$op2 = null; if($op2 == "-")$v1 = $v1 - ($v3 * $v2);$op2 = null; break; case "/": if($op2 == "+")$v1 = $v1 + ($v3 / $v2);$op2 = null; if($op2 == "-")$v1 = $v1 - ($v3 / $v2);$op2 = null; break; } } break; } // ausgabe des aktuellen strings echo $cur." "; } // nochmal n paar if´s, um das ganze erstmal zum laufen zu bringen if($op2 == "+")$v1 = $v1 + $v3; if($op2 == "-")$v1 = $v1 - $v3; // hier wird das ergebnis ausgegeben echo "= ".$v1; echo "</center>"; ?>
Das Problem ist, dass ich nach jeder * oder / Rechnung $v1 addiere/subtrahiere,
auch wenn das nachfolgende Rechenzeichen ein * oder / ist,
also hält er die PvS-rechnung quasi nur 1 mal hintereinander ein.
Ich freue mich über jede Idee die ihr einbringen könnt, beachtet bitte nur,
dass ich KEIN EVAL() benutzen darf.
grüße -
Diskutiere mit und stelle Fragen: Jetzt kostenlos anmelden!
lima-city: Gratis werbefreier Webspace für deine eigene Homepage
-
Du solltest du das ganze etwas strukturierter machen:
hier mal ein Ansatz(der Lerneffekt soll ja nicht auf meiner Seite stattfinden)
Du brauchst:
ein eindimensionales Array, in welchen die zahlen ohne die rechenzeichen stehen. $zahlen
ein anderes eindimensionales Array, indem die Rechenzeichen stehen. $zeichen
Die Array zu füllen sollte mit regEx eigenltich nicht so schwer sein.
$zeichen[0] ist dann also das rechenzeichen zwischen $zahlen[0] und $zahlen[1]
$zeichen[1] ist dann das zeichen zwischen $zahlen[1] und $zahlen[2]
Jetzt könntest du eine rekursive funktion nutzen, die deinen Term (also die beiden arrays) vereinfacht bis $zeichen leer ist und $zahlen nur noch eine Zahl enthält.
Du suchst nach nach einem * ,erechnest das produkt der Zahl davor und der Zahl danach und ersetzt die erste Zahl mit diesem Ergebniss, das * löscht du dann aus dem array und beim anderen array löscht du die zweite zahl.
unter php.net findest du einige array funktionen die helfen können.
Dann machst du das ganze mit /, dann mit + und dann mit -
Bin mir nicht ganz sicher, ob das funktioniert, hab sowas auch noch nicht gemacht. Wirkt auf mich aber ganz brauchbar.
Beitrag zuletzt geändert: 28.8.2013 16:48:13 von unlikus -
Mein Ansatz wäre, die Formel in einen Baum zu parsen.
Je höher das Level eines Statements ( ergo Zahl Operation Zahl* ), desto früher muss es ausgeführt werden.
Daraus resultiert ein recht einfach zu iterierender Baum bei dem Punkt vor Strichrechnung bereits integriert ist. Die Schwierigkeit ist natürlich erstmal den Baum zu erstellen, aber wie das geht sollst du ja rausfinden. :)
* Naja nicht ganz.
Ein valides Statement bspw wäre auch -2
Also negative Zahlen. Andererseits kann man das auch wieder als (0-1) * 2 interpretieren, wenn man das wirklich möchte. :P
Beitrag zuletzt geändert: 28.8.2013 17:08:53 von adrians -
hey, du hast ja auch tft in deinem Namen :)
ich würde das ganze so machen
<?php function calc($data) { $split = preg_split('~([\+\-])~', $data, 2, PREG_SPLIT_DELIM_CAPTURE); if (count($split) == 1) { $split = preg_split('~([*/])~', $data, 2, PREG_SPLIT_DELIM_CAPTURE); if (count($split) == 1) { return (double)$data; } } list($left, $op, $right) = $split; switch($op) { case '*': return calc($left) * calc($right); case '/': return calc($left) / calc($right); case '+': return calc($left) + calc($right); case '-': return calc($left) - calc($right); } trigger_error("invalid: " .$data); } var_dump(calc("30+8*27-15/3")); ?>
-
hab nochmal mit meinem Chef geredet: Bsp.: 1*(5+(3-2)*1)+5
-Ich nehme den String, suche nach der letzten öffnenden Klammer und speichere ihren Stellenwert (6) in einer Valiable,
-anschließend suche ich nach der ersten schließenden Klammer die einen Stellenwert von > 6 hat (10)
-mit einer for-schleife suche ich alle zahlen und zeichen zwischen 6 und 10 heraus und speichere sie in eine variable die ich in die funktion calculate() einsetze (rechnet mit PvS-rechnung den string aus)
-diesen ausgerechneten wert setze ich statt den Klammern ein, also muss an stelle 6 dieses erste Zwischenergebnis eingesetzt werden und alle danach kommenden stellen vorrücken(11-15 auf 7-11)
-das ganze noch in eine while-schleife solange bis der dritte Stellenwert des strings nicht mehr vorhanden ist (der 3. weil "-3" drin stehen könnte)
-die ersten 3 Schritte sollte ich hinbekommen, beim 4. weiß ich nich wie ich dieses "nachrutschen" realisieren soll, der 5. schritt ist ja nich viel... :) wär cool wenn ihr mir nochmal helfen könnt
grüße Toben :D
@tft-development:
echt cool, aber da sind noch ein paar Fehler drin trotzdem danke :) -
Du könntest dir auch den Shunting-yard Algorithmus angucken.
-
Diskutiere mit und stelle Fragen: Jetzt kostenlos anmelden!
lima-city: Gratis werbefreier Webspace für deine eigene Homepage