Calculer une operation presente dans une string (gestion des nombres a virgules)

Soyez le premier à donner votre avis sur cette source.

Snippet vu 8 734 fois - Téléchargée 29 fois

Contenu du snippet

Ce code permet de calculer une operation basique presentent dans une chaine de caractere, comme par exemple (3*5(10-2))/10 (qui renvoi 12)
Cependant il ne prend pas en compte les priorité de multiplications et de division, et donc 5+3*2 revien pour lui à 8*2 (pour lui faire comprendre faut faire 5+(3*2))

il gere aussi les float (type single), donc 3*3.33333333333 = 10 ou preske :P

je mets pas de zip, il suffit de faire un copier coller dans un fichier

Source / Exemple :


(*--------------------------------------------//
  Calculatrice de chaines (resultat a virgule)
  Par anK (legere modif. de Bestiol :))
  Bug(s): lorsqu'on divise par 0... :D
//--------------------------------------------*)

unit Unit1;

interface

uses strUtils,sysUtils;

type
TOperation=(loAdd,loSub,loMul,loDiv);

function CalculStr(Str:String):Single;

const
Niveau_Parentheses=15;

implementation

function SimplifyStr(Str:string):string;
const
  Smpl:    Array[0..3]Of String    = ('++', '--', '+-', '-+');  //: Tableau des doublons à simplifier
  SmplPar: Array[0..3]Of String    = ('+' , '+' , '-' , '-');//: Par quoi il faut les remplacer
 
var i: Integer;
 
begin
  //ça bug encore si il y a --- ou +++ ect... mais a part ça c nikel ;)
  //Plus maintenant ! ;o)
  Result := Str;

  for i := Low(Smpl) to High(Smpl) do
    While Pos(Smpl[i], Result) > 0 do
      Result := AnsiReplaceStr(Result, Smpl[i], SmplPar[i]);
 
  Result := AnsiReplaceStr(Result, '*(', '(');
  Result := AnsiReplaceStr(Result, '()','');
  Result := AnsiReplaceStr(Result, ' ',''); //On comble les trous!
  Result := AnsiReplaceStr(Result, '.',','); //Les . sont transformés en ,
end;

function CalculStr(Str:String):Single;
var
  I,J:integer;
  LastOperande: TOperation;
  zPar:         array[0..Niveau_Parentheses] of single; //Profondeur des parentheses
  tPar:         array[1..Niveau_Parentheses] of TOperation; //Type liason entre la parenthese et sa superieure
  PeC:          0..Niveau_Parentheses;   //Parenthese en Cours
  fstOp:        boolean; //Premier operateur
  ActuNombre:   boolean; //Si actuellement il y a un nombre...
  TailleNombre: Word;
  DebutNombre:  Word;
  StringTemp:   string;
  DejaVirgule:  boolean; //Si le nombre a deja une virgule...

  procedure CalculUneOperation;
  var Nombre:single;
  begin
    Nombre:=StrtoFloat(Copy(StringTemp,DebutNombre,TailleNombre));
    case LastOperande of
      loAdd:
        zPar[PeC]:=zPar[PeC]+Nombre;
      loSub:
        zPar[PeC]:=zPar[PeC]-Nombre;
      loMul:
        zPar[PeC]:=zPar[PeC]*Nombre;
      loDiv:
        zPar[PeC]:=zPar[PeC]/Nombre;
    end;
  end;

  procedure StopNombre;
  begin
    DejaVirgule   := False;
    ActuNombre    := False;
  end;

  procedure InitParenthese;
  begin

  end;

const
  Nombre     =   ['0'..'9']; //On a dans nombre aussi le '.', mais il est comptabilisé differement :)
  Signe      =   ['+','-','*','/'];
  Parenthese =   ['(',')'];
begin
//-------------Initialisation-----------------
  LastOperande  := loAdd;
  fstOp         := True;
  PeC           := 0;
  zPar[0]       := 0;
  ActuNombre    := False;
  DejaVirgule   := False;
//--------------------------------------------
  StringTemp    := SimplifyStr(Str);   //Simplification des calculs (++ devient + ect...)
  for I:=1 to Length(Str) do   //Debut de la boucle
  begin

//-------------Les Nombres -------------------
  if StringTemp[I] in Nombre then
  begin
    if not ActuNombre then  //Si un nombre n'est pas deja "Lancé"
    begin
    //On le "lance"
    ActuNombre  :=  True;
    DebutNombre :=  I;
    fstOp:=False;
    end;
  end;
//--------------------------------------------

//-------------La virgule --------------------
  if StringTemp[I] = ',' then
  begin
    if (not DejaVirgule) and ActuNombre then
    begin
    //On rajoute la virgule au nombre deja lancé
    DejaVirgule:=True; // =)
    end else begin
    //ERREUR dans la string meme...
    end;
  end;
//--------------------------------------------

//---------Parentheses ouvertes---------------

  if StringTemp[I] = '(' then
  begin
    if ActuNombre then//Si ActuNombre = True alors on coupe le nombre...
    begin
      TailleNombre  := I-DebutNombre;
      ActuNombre    := False;
      CalculUneOperation; //On calcul la derniere operation
      inc(PeC);
      zPar[PeC]     :=  0; //on reinitialise l'interieur de la parenthese
      //Donc si il y a un nombre k(x) on a k*x
      tPar[PeC]     := loMul;
      //zPar[PeC] :=  0;
      //Comme dans toute parenthese, le FirstOperateur=true, et on commence par un add
      LastOperande  := loAdd;
      fstOp         := True;
    end else
    begin
      inc(PeC);
      zPar[PeC]     :=  0; //on reinitialise l'interieur de la parenthese
      tPar[PeC]     := LastOperande;//Le dernier operateur avant la parenthese
      LastOperande  := loAdd;
      fstOp         := True;
    end;
  end;

//---------Parentheses fermé---------------

  if StringTemp[I] = ')' then
  begin
    //On s'assure la finalisation du dernier nombre de la parenthese
    if ActuNombre then
    begin
    TailleNombre:=I-DebutNombre;
    CalculUneOperation;  //On finalise si jamais il resté un nombre...
    end;

    case tPar[PeC] of //On mets à jour la parenthese inferieur
      loMul:zPar[PeC-1]:=zPar[Pec-1]*zPar[Pec];
      loAdd:zPar[PeC-1]:=zPar[Pec-1]+zPar[Pec];
      loSub:zPar[PeC-1]:=zPar[Pec-1]-zPar[Pec];
      loDiv:zPar[PeC-1]:=zPar[Pec-1]/zPar[Pec];
    end;
    if ActuNombre then//Si ActuNombre = True alors on coupe le nombre...
    begin
      TailleNombre:=I-DebutNombre;
      ActuNombre:=False;
      CalculUneOperation; //On calcul la derniere operation
      //Donc si il ya un nombre k(x) on a k*x
      //LastOperande:=loMul; //Donc on multiplie ;)
    end;
      dec(PeC);
  end;

//--------------------------------------------

//-------------Le signe Plus-----------------

    if StringTemp[I] = '+' then
    begin

      if fstOp or (StringTemp[I-1] in ['*','/']) then //Si Premier calcul ou avant le - il y avait un * ou /
      begin
      //C'est le premier operateur, donc un debut de nombre...
        if StringTemp[I+1] in Nombre then //On debute un nombre...
        begin
          fstOp       :=  False;
          ActuNombre  :=  True;
          DebutNombre :=  (I+1);//Sans prendre en compte le +
        end;
      end;

      if ActuNombre then //Si un nombre est déja entrain d'etre lu
      begin
        ActuNombre    :=  False; //On stoppe la lecture de ce nombre
        TailleNombre  :=  I-DebutNombre;
        CalculUneOperation; //On calcul le dernier Operateur
        LastOperande  :=  loAdd; //On change ce dernier operateur :)
      end;

      if not fstOp then if StringTemp[I-1]=')' then //Si le dernier caractere été un ')' alors...
      begin
        //A la difference avec ActuNombre=true, on a ici pas de nombre, mais une parenthese...
        //Donc pas de nombre à definir
        LastOperande  :=  loSub; //On change seulement ce dernier operateur :)
      end;

    end;

//--------------------------------------------

//-------------Le signe Moins-----------------

    if StringTemp[I] = '-' then
    begin

      if ActuNombre then //Si un nombre est deja entrain d'etre lu
      begin
        ActuNombre    :=  False; //On stoppe la lecture de ce nombre
        TailleNombre  :=  I-DebutNombre;
        CalculUneOperation; //On calcul le dernier Operateur
        LastOperande  :=  loSub; //On change ce dernier operateur :)
      end;

      if fstOp or (StringTemp[I-1] in ['*','/']) then //Si Premier calcul ou avant le - il y avait un * ou /
      begin
      //C'est le premier operateur, donc un debut de nombre...
        if StringTemp[I+1] in Nombre then //On debute un nombre...
        begin
          fstOp       :=  False;
          ActuNombre  :=  True;
          DebutNombre :=  I;
        end;
      end;

      if not fstOp then if StringTemp[I-1]=')' then //Si le dernier caractere été un ')' alors...
      begin
        //A la difference avec ActuNombre=true, on a ici pas de nombre, mais une parenthese...
        //Donc pas de nombre à definir
        LastOperande  :=  loSub; //On change seulement ce dernier operateur :)
      end;

    end;

//--------------------------------------------

//-------------Le signe Multiplication--------

    if StringTemp[I] = '*' then
    begin

      if ActuNombre then //Si un nombre est deja entrain d'etre lu
      begin
        ActuNombre    :=  False; //On stoppe la lecture de ce nombre
        TailleNombre  :=  I-DebutNombre;
        CalculUneOperation; //On calcul le dernier Operateur
        LastOperande  :=  loMul; //On change ce dernier operateur :)
      end;

      if not fstOp then if StringTemp[I-1]=')' then //Si le dernier caractere été un ')' alors...
      begin
        //A la difference avec ActuNombre=true, on a ici pas de nombre, mais une parenthese...
        //Donc pas de nombre à definir
        LastOperande  :=  loMul; //On change seulement ce dernier operateur :)
      end;

    end;

//--------------------------------------------

//-------------La Division--------------------

    if StringTemp[I] = '/' then
    begin

      if ActuNombre then //Si un nombre est deja entrain d'etre lu
      begin
        ActuNombre    :=  False; //On stoppe la lecture de ce nombre
        TailleNombre  :=  I-DebutNombre;
        CalculUneOperation; //On calcul le dernier Operateur
        LastOperande  :=  loDiv; //On change ce dernier operateur :)
      end;

      if not fstOp then if StringTemp[I-1]=')' then //Si le dernier caractere été un ')' alors...
      begin
        //A la difference avec ActuNombre=true, on a ici pas de nombre, mais une parenthese...
        //Donc pas de nombre à definir
        LastOperande  :=  loDiv; //On change seulement ce dernier operateur :)
      end;

    end;

//--------------------------------------------

  end;

  if ActuNombre then
  begin
  TailleNombre:=Length(StringTemp)-I;
  CalculUneOperation;  //On finalise si jamais il resté un nombre...
  end;
  Result:=zPar[0]; //On fou dans result le nombre trouvé...
end;

end.

Conclusion :


exemple d'utilisation: ShowMessage(Floattostr(CalculStr('8/10')));

Si jamais des bugs apparaissent (et il doit y en avoir) merci de me le dire :)

A voir également

Ajouter un commentaire

Commentaires

jfkca
Messages postés
1
Date d'inscription
vendredi 26 décembre 2003
Statut
Membre
Dernière intervention
26 décembre 2003
-
Salut balgrim

En faisant des essais avec ton code j'ai fait les deux modifications suivant pour que si je calcul (1+1)(2+3) il renvoi 10 et non 7.

Remplacer "Result := AnsiReplaceStr(Result, '*(', '(');" par "Result := AnsiReplaceStr(Result, ')(', ')*(');" ainsi que


"for I:=1 to Length(Str) do //Debut de la boucle"
par "for I:=1 to Length(StringTemp) do //Debut de la boucle"


Dans l'initialisation des variable j'ai aussi fait une petite modification pour être certain de la variable ZPar soit bien initialisée
Remplacer "ZPar[0]:=0"
Par
For I:=0 To Niveau_Parentheses Do
Begin
zPar[I] := 0;
End;

La variable J n'est jamais utilisé on peut donc la supprimer
Il ne reste plus qu'a gérer la priorité.

Voilà bon coding pour la gestion des prioritées
balgrim
Messages postés
52
Date d'inscription
vendredi 26 avril 2002
Statut
Membre
Dernière intervention
28 octobre 2003
-
A propos de l'autre source, je m'été renseigné, mais si tu faisait un petit peu plus attention, tu verrai qu'elle ne gere pas les nombre a virgules, mais seulement les integer.
En plus elle est beaucoup plus lente à l'execution, et ça aller pas pour ce que je voulais faire :)

Donc non tu casse pas la baraque, c'est meme deux sources complementaires...
yoghisan
Messages postés
221
Date d'inscription
samedi 10 mai 2003
Statut
Membre
Dernière intervention
2 juin 2005
1 -
oups pas manchester mais balgrim
balgrim
Messages postés
52
Date d'inscription
vendredi 26 avril 2002
Statut
Membre
Dernière intervention
28 octobre 2003
-
Oui j'avais pensé à la fin de ligne, mais le probleme est toujours le meme ;) 8+3*2+4 ==> on revien au meme pb... La meilleur solution c de gerée la multiplication et la division comme une parenthese, sa doit pas etre trop difficile :)

Sinon j'ai remarquer une erreur, si on dans la chaine ...)+... comme (2*5)+3 renvoi 7. c'est du au fait d'une erreur dans le code:


//-------------Le signe Plus-----------------
(...)
if not fstOp then if StringTemp[I-1]=')' then //Si le dernier caractere été un ')' alors...
LastOperande := loSub; <<--Modifier par loAdd
//---------------------------------------------

J'ameliore tout ça, et je v mettre un zip tout de meme, avec des jolie exemples :)
cs_ManChesTer
Messages postés
378
Date d'inscription
vendredi 20 octobre 2000
Statut
Modérateur
Dernière intervention
11 décembre 2013
-
Tu te complique la vie le plus simple pour analyser ce genre de calcul est de commancer par la fin de la ligne ...

Exemple :
8+3*2 => 8+6 => 14 et hop...

Bon Coding...

ManChesTer.

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.