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

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

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.