kostenloser Webspace werbefrei: lima-city


php string berechnen ohne eval()

lima-cityForumProgrammiersprachenPHP, MySQL & .htaccess

  1. Autor dieses Themas

    t**********e

    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
  2. Diskutiere mit und stelle Fragen: Jetzt kostenlos anmelden!

    lima-city: Gratis werbefreier Webspace für deine eigene Homepage

  3. 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
  4. 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
  5. tft-development

    Kostenloser Webspace von tft-development

    tft-development hat kostenlosen Webspace.

    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"));
    ?>
  6. Autor dieses Themas

    t**********e



    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 :)
  7. Du könntest dir auch den Shunting-yard Algorithmus angucken.
  8. Diskutiere mit und stelle Fragen: Jetzt kostenlos anmelden!

    lima-city: Gratis werbefreier Webspace für deine eigene Homepage

Dir gefällt dieses Thema?

Über lima-city

Login zum Webhosting ohne Werbung!