IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage Delphi Discussion :

Remplacement de chaine rapide


Sujet :

Langage Delphi

  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Janvier 2009
    Messages
    322
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : Janvier 2009
    Messages : 322
    Points : 310
    Points
    310
    Par défaut Remplacement de chaine rapide
    bonjour à tous

    Je cherche à optimiser une routine qui efface des caractères spécifique dans une chaine de caractères soit

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    function EffaceCars0(Chaine,Caracteres:string):string;
    var i:integer;
    begin
        result:=chaine;
        for i:=1 to length(Caracteres) do
           result:=fastreplace(result,Caracteres[i],'',True);
    end;
    Il y a longtemps Francois Piette avait montrer comment optimiser en travaillant directement sur les pointeurs de caractères.

    Je l'ai égaré...

    Le principe est très simple, mais sans les commandes exactes ...

    Ça devrait ressembler à quelque chose comme ça :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    function EffaceCars(Chaine,Caracteres:string):string;
    var i,ix,i0:pointer;
        j,jx,j0:pointer;
        k:pointer;
        l:integer;
    begin
        result:=Chaine;//fait une copie pour avoir la bonne longueur tout de suite
        i0:=pchar(Chaine[1]);
        ix:=pchar(Chaine[length(Chaine)]);
     
        j0:=pchar(Caracteres[1]);
        jx:=pchar(Caracteres[length(Caracteres)]);
     
        l:=0;//longueur du résultat
        k:=i0;//adresse ou enregistre le prochain caractère a conserver
     
        i:=i0;
        while i<=ix do begin//tant que le pointeur en cour sur la chaine est inférieur à son caractère maximum
           j:=j0;//initialise au premier caractère à conserver
           while j<=jx do begin //tant que le pointeur en cour sur les caractères est inférieur à son caractère maximum
               if i^=j^ then begin //si les caractère sont identique alors
                   k^:=j^;//transfert le caractère à sa place
                   inc(k);//incrément le pointeur sur le caractère suivant
                   inc(l);//incrémente la longueur du résultat
                   break;//sort de la boucle
               end;
               inc(j);//passe au caractère suivant des caractères à conserver
           end;
           inc(i);//passe au caractère suivant de la chaine a criblée
        end;
        setlength(result,l);//ajuste la longueur du résultat
    end;
    mais ça marche poh
    Au premier while il refuse de compiler disant qu'il
    "[Erreur] ES_.pas(314): Opérateur non applicable à ce type d'opérande"

    J'aimerais aussi utiliser des boucles pour (for) à la place des tant que (while) marche poh non plus

    Merci d'avance pour votre aide.

  2. #2
    Expert éminent sénior
    Avatar de Paul TOTH
    Homme Profil pro
    Freelance
    Inscrit en
    Novembre 2002
    Messages
    8 964
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Freelance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2002
    Messages : 8 964
    Points : 28 430
    Points
    28 430
    Par défaut
    sans se compliquer la vie, tu peux utiliser un PChar au lieu d'un string indexé.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
     
    var
      s: string;
      p: PChar;
      i: Integer;
    begin
      for i := 1 to Length(s) do
       s[i] := 'a';
    // équivalent sans indexe
      p := @s[1];
      for i := 1 to Length(s) do
      begin
        p^ := 'a';
       Inc(p);
      end;
    end;
    cependant je ne suis pas convaincu du gain de performance sur un PC récent...par contre la réallocation de la chaîne coûte chère.

    voici une façon de faire

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
     
    var
      i: Integer;
      c: Char;
      x: Integer;
    begin
      SetLength(Result, Length(Chaine)); // une seule allocation
      x := 0;
      for i := 1 Length(Chaine) do
      begin
        c := Chaine[i];
        if Pos(c, caracteres) = 0 then
        begin
          Inc(x);
          Result[x] := c;
        end;
      end;
      SetLength(Result, x); // une seule reallocation
    end;
    il est aussi possible de calculer la taille finale avant allocation en comptant le nombre de caractères qu'il faudra supprimer (une première boucle sans modification de Result)

    pour éviter le Pos() on peut aussi tester chaque caractère individuellement...les performances vont dépendre de la longueur des chaînes, a-t-on bcp de caractères à supprimer ou les chaînes à nettoyer sont-elles longues
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
     
    var
      i: Integer;
      c: Char;
      x: Integer;
      l: Integer;
      r: Char;
    begin
      Result := Chaine;
      l := Length(Result);
      for j := 1 to Length(Caracteres) do
      begin
        r := Caracteres[j];
        x := 0;
        for i := 1 to l do
        begin
          c := Result[i];
          if c <> r then
          begin
            Inc(x);
            Result[x] := c;
          end;
        end;
        l := x;
     end;
      SetLength(Result, l);
    end;
    et là encore chaque String[x] peut être remplacé par un PChar^ qu'on incrémente

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
     
    var
      i: Integer;
      c: Char;
      x: Integer;
      l: Integer;
      r: Char;
      s: PChar;
      p: PChar;
    begin
      Result := Chaine;
      l := Length(Result);
      for j := 1 to Length(Caracteres) do
      begin
        r := Caracteres[j];
        x := 0;
        s := @Result[1];
        p := s;
        for i := 1 to l do
        begin
          c := s^;
          Inc(s);
          if c <> r then
          begin
            Inc(x);
            p^ := c;
            Inc(p);
          end;
        end;
        l := x;
     end;
      SetLength(Result, l);
    end;
    Developpez.com: Mes articles, forum FlashPascal
    Entreprise: Execute SARL
    Le Store Excute Store

  3. #3
    Membre averti
    Profil pro
    Inscrit en
    Janvier 2009
    Messages
    322
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : Janvier 2009
    Messages : 322
    Points : 310
    Points
    310
    Par défaut
    Merci Paul pour ton aide, tu as sorti de bonnes astuces sans compter les punaises qu'il y avait dans mon code original

    le code final est le suivant:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
        function EffaceCars(const Chaine,Caracteres:string):string;
    var ip,jp,j0,rp:pchar;
        l:integer;
        i,j,ix,jx:integer;
        h:char;
        Concerve:boolean;
    begin
        jx:=length(Caracteres);if jx=0 then exit;//sort sur chaine vide et ne garde donc aucun caractere
        ix:=length(Chaine);    if ix=0 then exit;//sort sur chaine vide
        result:=Chaine;//fait une copie pour avoir la bonne longueur tout de suite
     
        rp:=@(Result[1]);//adresse ou enregistre le prochain caractere a conserver
        ip:=rp;
        j0:=@(Caracteres[1]);//
        l:=0;//longueur du resultat
     
        for i:=1 to ix do begin//tant que le pointeur en cour sur la chaine est inferieur à son caractere maximum
            h:=ip^;
            jp:=j0;//initialise au premier caractère à conserver
            Concerve:=true;
            for j:=1 to jx do begin //tant que le pointeur en cour sur les caractères est inferieur à son caractere maximum
                if h=jp^ then begin //si les caractere sont identique alors
                    concerve:=false;
                    break;  //sort de la boucle
                end;
               inc(jp);//passe au caractère suivant des caractères à conserver
           end;
           if concerve then begin
               inc(l);
               rp^:=ip^;
               inc(rp);
           end;
           inc(ip);//passe au caractère suivant de la chaine a criblée
        end;
        setlength(result,l);//ajuste la longueur de résultat
    end;
    Comparaison des temps d'exécution : gain de 80% i.e. la nouvelle routine est cinq fois plus rapide que l'ancienne.(Approximatif car plus il y a de caractères potentiels à effacer plus c'est à l'avantage de la nouvelle routine) avec le code suivant:


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
     
    const
         a='123456789012345678901234567890123456789012345678901234567890';
         c='90a';
     
    procedure TForm1.Button1Click(Sender: TObject);
    var i:integer;
        d1:tdatetime;
        r:string;
    begin
        d1:=now;
        for i:=1 to 10000000 do begin
           r:=effacecars(a,c);
        end;
        memo1.Lines.add(r+' '+timetostr(d1-now));
        d1:=now;
        for i:=1 to 10000000 do begin
           r:=effacecars0(a,c);
        end;
        memo1.Lines.add(r+' '+timetostr(d1-now));
    end;

  4. #4
    Expert éminent
    Avatar de Lung
    Profil pro
    Analyste-programmeur
    Inscrit en
    Mai 2002
    Messages
    2 664
    Détails du profil
    Informations personnelles :
    Âge : 43
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Analyste-programmeur
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mai 2002
    Messages : 2 664
    Points : 6 961
    Points
    6 961
    Par défaut
    Citation Envoyé par Paul TOTH Voir le message
    sans se compliquer la vie, tu peux utiliser un PChar au lieu d'un string indexé.
    Tu pourrais expliquer pourquoi c'est plus rapide ?
    (les pointeurs, c'est pas mon fort)

    J'ai modifié un bout de code que j'utilise pour formater le contenu d'un RichEdit, et c'est devenu quasi-instantané.
    Dans mon for, j'ai remplacé RichEdit.Lines.Text[i] par p^, sachant que p := @RichEdit.Lines.Text[1];
    L'urgent est fait, l'impossible est en cours, pour les miracles prévoir un délai. ___ Écrivez dans un français correct !!

    C++Builder 5 - Delphi 6#2 Entreprise - Delphi 2007 Entreprise - Delphi 2010 Architecte - Delphi XE Entreprise - Delphi XE7 Entreprise - Delphi 10 Entreprise - Delphi 10.3.2 Entreprise - Delphi 10.4.2 Entreprise - Delphi 11.1 Entreprise
    OpenGL 2.1 - Oracle 10g - Paradox - Interbase (XE) - PostgreSQL (15.4)

  5. #5
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 447
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 447
    Points : 24 849
    Points
    24 849
    Par défaut
    Pour une string, cela évite des appels routines implicite comme UniqueString() qui provoque des ré-allocations, le pointeur évite aussi quelques opérations arithmétiques liées à l'opérateur [], ...

    Pour Lines.Text, là c'est encore plus visible, cela n’appelle plus le Setter SetText du TRichEditStrings, cela n'invoque plus un tas d'API windows lié au RichEdit, c'est évidemment plus rapide

    Sinon, il y a peine quelques jours, on a eu le sujet remplacer les caractères spéciaux d'un string ? contenant un lien vers Comment supprimer tous les caractères spéciaux d'une string ? dont ma fonction CopyStringOnly optimisée à l'origine pour D6 ainsi qu'une approche inverse DeleteFromStr utilisant un TSysCharSet qui est plus lisible comme type qu'une chaine énumérant les caractères et permet d'utiliser l'opérateur in sur un set, ce qui doit être plus performant (a vérifier) si l'on souhaite supprimer un grand nombre de caractère par rapport à une double itération for

    Citation Envoyé par ShaiLeTroll Voir le message
    Code inspiré des algos combinés de Fabrice ROUXEL + SJRD :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    function DeleteFromStr(const Str : string; Excluded : TSysCharSet) : string;
    var Len, I, J : integer;
    begin
      Len := Length(Str);
      SetLength(Result, Len);
      J := 0;
      for I := 1 to Len do
      begin
        if not (Str[i] in Excluded) then
        begin
          inc(J);
          Result[J] := Str[i];
        end;
      end;
      SetLength(Result, J);
    end;
    Dans ton cas les CharSet risque d'être très long, ainsi l'approche inverse sera plus pratique avec ma fonction :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    ResStr := CopyStringOnly('cou4co75u 4l782a 4523Fran45234c789e34!', ['a'..'z', 'A'..'Z', ' ', '!']);

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    function CopyStringOnly(const Str : string; const OnlyChars : TSysCharSet): string;
    type
      TSCSALOON = record
        case Boolean of
          True: (SCS: TSysCharSet);
          False: (A: Array[1..8] of Cardinal);
        end;
    var
       Len, I : integer;
       J, P: PChar;
    begin
      Len := Length(Str);
      if Len = 0 then
      begin
        Result := '';
        Exit;
      end;
      with TSCSALOON(OnlyChars) do
      begin
        if (A[1] = 0) and (A[2] = 0) and (A[3] = 0) and (A[4] = 0) and (A[5] = 0) and (A[6] = 0) and (A[7] = 0) and (A[8] = 0) then
        begin
          Result := '';
          Exit;
        end;
      end;
     
      SetLength(Result, Len);
      P := @Result[1];
      J := P;
      for I := 1 to Len do
      begin
        if Str[i] in OnlyChars then
        begin
          J^ := Str[i];
          Inc(J);
        end;
      end;
      SetLength(Result, Cardinal(J) - Cardinal(P));
    end;
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  6. #6
    Expert éminent sénior
    Avatar de Paul TOTH
    Homme Profil pro
    Freelance
    Inscrit en
    Novembre 2002
    Messages
    8 964
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Freelance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2002
    Messages : 8 964
    Points : 28 430
    Points
    28 430
    Par défaut
    Citation Envoyé par Lung Voir le message
    Tu pourrais expliquer pourquoi c'est plus rapide ?
    (les pointeurs, c'est pas mon fort)

    J'ai modifié un bout de code que j'utilise pour formater le contenu d'un RichEdit, et c'est devenu quasi-instantané.
    Dans mon for, j'ai remplacé RichEdit.Lines.Text[i] par p^, sachant que p := @RichEdit.Lines.Text[1];
    alors comme le dit Shai là c'est autre chose, tu peux comparer avec s[i] où s est un string initialisé par RichEdit.Lines.Text; l'extraction du texte ne se fait qu'une fois et non à chaque itération et il est stocké explicitement dans un string alors qu'avec ton code on ne sait pas trop où pointe p...une variable temporaire probablement libérée à mon avis.
    Developpez.com: Mes articles, forum FlashPascal
    Entreprise: Execute SARL
    Le Store Excute Store

  7. #7
    Membre averti
    Profil pro
    Inscrit en
    Janvier 2009
    Messages
    322
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : Janvier 2009
    Messages : 322
    Points : 310
    Points
    310
    Par défaut Le gagnant est...
    ShaiLeTroll

    Routine ~50% plus rapide.

    Bravo !!!

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     d1:=now;
        for i:=1 to 40000000 do begin
           r:=effacecars(a,c);
        end;
        memo1.Lines.add(r+' '+timetostr(d1-now));
     
        d1:=now;
        for i:=1 to 40000000 do begin
           r:=DeleteFromStr(a,ensemble);
        end;
        memo1.Lines.add(r+' '+timetostr(d1-now));
    Ce résultat est pour 4 lettres à effacer
    Pour 5 lettres c'est 2 X + rapide !!!

  8. #8
    Membre averti
    Profil pro
    Inscrit en
    Janvier 2009
    Messages
    322
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : Janvier 2009
    Messages : 322
    Points : 310
    Points
    310
    Par défaut
    @Shai

    Dans le code de la seconde routine que tu proposes :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    function CopyStringOnly(const Str : string; const OnlyChars : TSysCharSet): string;
    type
      TSCSALOON = record
        case Boolean of
          True: (SCS: TSysCharSet);
          False: (A: Array[1..8] of Cardinal);
        end;
    var
       Len, I : integer;
       J, P: PChar;
    begin
      Len := Length(Str);
      if Len = 0 then
      begin
        Result := '';
        Exit;
      end;
      with TSCSALOON(OnlyChars) do
      begin
        if (A[1] = 0) and (A[2] = 0) and (A[3] = 0) and (A[4] = 0) and (A[5] = 0) and (A[6] = 0) and (A[7] = 0) and (A[8] = 0) then
        begin
          Result := '';
          Exit;
        end;
      end;
     
      SetLength(Result, Len);
      P := @Result[1];
      J := P;
      for I := 1 to Len do
      begin
        if Str[i] in OnlyChars then
        begin
          J^ := Str[i];
          Inc(J);
        end;
      end;
      SetLength(Result, Cardinal(J) - Cardinal(P));
    end;
    À quoi sert le code suivant:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    type
      TSCSALOON = record
        case Boolean of
          True: (SCS: TSysCharSet);
          False: (A: Array[1..8] of Cardinal);
        end;
    var
    ...
    begin
    ... 
      with TSCSALOON(OnlyChars) do
      begin
        if (A[1] = 0) and (A[2] = 0) and (A[3] = 0) and (A[4] = 0) and (A[5] = 0) and (A[6] = 0) and (A[7] = 0) and (A[8] = 0) then
        begin
          Result := '';
          Exit;
        end;
      end;
    j'ai pigé que dalle ;-)

  9. #9
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 447
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 447
    Points : 24 849
    Points
    24 849
    Par défaut
    Dans CopyStringOnly, si le Set OnlyChars est vide autant renvoyer vide puisque l'on ne copiera rien
    Que ce soit CopyStringOnly ou DeleteFromStr, les performances seront similaires, c'est plus une question de Lisibilité ou de manière de voir les choses :

    CopyStringOnly : Je prends que ce que j'ai prévu
    DeleteFromStr : Je retire que ce que je n'ai pas prévu

    on avait pas, en D6, le moyen de connaitre le nombre d'élément sur un set et je ne crois pas que cela existe aujourd'hui

    sur un TSysCharSet avec AnsiChar on a le maximum de valeur possible pour un ensemble, 0..255 donc comme chaque bit du set indique le flag de AnsiChar correspondant, il faut 32 octet (8 DWORD) pour stocker 256 position, d'où TSCSALOON qui permet un transtypage du Set en Array de 8 DWORD, si tous Zéro donc Set vide

    TSCSALOON c'est SysCharSet \ Array \ LOON, je sais plus

    Dans le même principe : Comment calculer la cardinalité d'un ensemble ou trouver le nombre de bits "allumés" dans une variable ?


    On pourrait ainsi protéger DeleteFromStr d'un Excluded vide de la même façon

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    function DeleteFromStr(const Str : string; Excluded : TSysCharSet) : string;
    type
      TSCSALOON = record
        case Boolean of
          True: (SCS: TSysCharSet);
          False: (A: Array[1..8] of Cardinal);
        end;
    var 
      Len, I, J : integer;
    begin
      Len := Length(Str);
      if Len = 0 then
      begin
        Result := '';
        Exit;
      end;
      with TSCSALOON(OnlyChars) do
      begin
        if (A[1] = 0) and (A[2] = 0) and (A[3] = 0) and (A[4] = 0) and (A[5] = 0) and (A[6] = 0) and (A[7] = 0) and (A[8] = 0) then
        begin
          Result := Str; // Pas d'Exclus donc on prend tout !
          Exit;
        end;
      end;
     
     
      SetLength(Result, Len);
      J := 0;
      for I := 1 to Len do
      begin
        if not (Str[i] in Excluded) then
        begin
          inc(J);
          Result[J] := Str[i];
        end;
      end;
      SetLength(Result, J);
    end;
    DeleteFromStr est un code écrit par Fabrice ROUXEL et SJRD qui devrait être dans la FAQ que l'on débattu en 2007 mais qui ne semble pas avoir été publiée !
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  10. #10
    Membre averti
    Profil pro
    Inscrit en
    Janvier 2009
    Messages
    322
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : Janvier 2009
    Messages : 322
    Points : 310
    Points
    310
    Par défaut Plus simple
    Parfait

    J'ai bien compris

    À la place de

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    False: (A: Array[1..8] of Cardinal);
    ...
    if (A[1] = 0) and (A[2] = 0) and (A[3] = 0) and (A[4] = 0) and (A[5] = 0) and (A[6] = 0) and (A[7] = 0) and (A[8] = 0) then 
    begin...
    Je suggère

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    False: (A: Array[1..2] of int64);
    ...
    if A[1] OR A[2] = 0 then 
    begin
    Mais si il faut garder le type cardinal a tout prix alors

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    False: (A: Array[1..8] of Cardinal);
    ...
    if A[1] OR A[2] OR A[3] OR A[4] OR A[5] OR A[6] OR A[7] OR A[8] = 0 then 
    begin...

  11. #11
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 447
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 447
    Points : 24 849
    Points
    24 849
    Par défaut
    Euh, ce n'est pas bon
    Cardinal = 4 Octets
    Int64 = 8 Octets
    Donc c'est soit 8 x 4 ou 4 x 8 mais juste 2 !

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    False: (A: Array[1..4] of int64);
    ...
    if A[1] OR A[2] OR A[3] OR A[4] = 0 then 
    begin
    Je ne suis pas sûr ces OR compile sans parenthèse, donc on en revient à
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    False: (A: Array[1..4] of int64);
    ...
    if (A[1] = 0) and (A[2] = 0) and (A[3] = 0) and (A[4] = 0) then 
    begin
    enfin A[1] OR A[2] = 0 n'est pas la même chose que (A[1] = 0) and (A[2] = 0).
    Le résultat sera effectivement très proche si tous les bits sont a zéro, c'est bien vide mais en terme de compilation, cela n'a rien à voir et en perf, le OR binaire 32 ou 64 bit, doit être bien plus lent qu'un pauvre CMP\JZ
    Sans parler de la plateforme cible du compilateur, sur un 32 bit le OR 64Bits est-il plus vraiment plus rapide que de deux 2 OR 32Bits ?

    le OR de ta proposition est un OR binaire bit à bit

    le AND de ma proposition est AND logique, ce dernier bénéficie de l'optimisation qui consiste à ne pas faire d'Evaluation booléenne complète, ainsi dès que le premier test est faux, les autres ne sont pas évalué !

    Donc en conclusion si la syntaxe A[1] OR A[2] OR A[3] OR A[4] OR A[5]... compile
    elle est plus lente car nécessite le calcul de tout les OR un par un
    puis de la comparaison à Zéro ce qui est donc contre-performant
    face à un code d'évaluation "court-circuit" des expressions booléennes !

    Sinon on peut écrire cela via LongBool

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    if LongBool(A[1]) and LongBool(A[2]) and LongBool(A[3]) and LongBool(A[4]) and LongBool(A[5]) and LongBool(A[6]) and LongBool(A[7]) and LongBool(A[8]) then
    Ce n'est pas forcément plus lisible, peut-être que le code ASM est légèrement différent mais Delphi est assez retord pour générer un code optimisé sur une égalité à zéro à coup de JZ !
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  12. #12
    Membre averti
    Profil pro
    Inscrit en
    Janvier 2009
    Messages
    322
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : Janvier 2009
    Messages : 322
    Points : 310
    Points
    310
    Par défaut ;)
    J'y penserai à deux fois avant de te suggérer des améliorations.

    Tu es vraiment trop fort.

    Merci encore pour ton aide

  13. #13
    Modérateur

    Homme Profil pro
    Ingénieur retraité
    Inscrit en
    Octobre 2005
    Messages
    2 396
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur retraité

    Informations forums :
    Inscription : Octobre 2005
    Messages : 2 396
    Points : 3 263
    Points
    3 263
    Par défaut
    Bonjour,

    Comme le test avec TSCSALOON(OnlyChars) m'avait intrigué j'ai fait un essai de comparaison pour avoir une idée du gain de speed par rapport à un simple if OnlyChars=[] then EXIT :

    Pour nbToursDeBoucle := 10000000, Str := 'cou4co75u 4l782a 4523Fran45234c789e34!' et OnlyChars := ['a'..'z', 'A'..'Z', ' ', '!'] :
    - Avec CopyStringOnly et test avec TSCALOON : Mis 2106 ms
    - Avec CopyStringOnly et test avec le if OnlyChars=[] then EXIT : Mis 2090 ms

    Conclusion : on dirait que le if OnlyChars=[] est déjà optimisé en arrière plan...
    et ceci peut-être en utilisant l'équivalent du TSCALOON ???.

    A+.
    N'oubliez pas de consulter les FAQ Delphi et les cours et tutoriels Delphi

  14. #14
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 447
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 447
    Points : 24 849
    Points
    24 849
    Par défaut
    Très intéressant Gilbert Geyer !
    Je n'avais jamais pensé au = sur un ensemble, je pensais que seul l'opérateur in était disponible pour un Set
    C'est tellement simple set = [] ...

    En C++Builder, le == est facilement visible dans la doc, il fallait juste chercher plus en Delphi : Opérateurs d'ensembles Expressions (Delphi)

    Il serait intéressant de voir le code ASM et de comprendre ce que cela donne en pseudo-Delphi
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  15. #15
    Modérateur
    Avatar de tourlourou
    Homme Profil pro
    Biologiste ; Progr(amateur)
    Inscrit en
    Mars 2005
    Messages
    3 856
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Biologiste ; Progr(amateur)

    Informations forums :
    Inscription : Mars 2005
    Messages : 3 856
    Points : 11 290
    Points
    11 290
    Billets dans le blog
    6
    Par défaut
    Un ensemble est stocké sous la forme d'un champ de Bits ; la comparaison est donc triviale.
    Delphi 5 Pro - Delphi 11.3 Alexandria Community Edition - CodeTyphon 6.90 sous Windows 10 ; CT 6.40 sous Ubuntu 18.04 (VM)
    . Ignorer la FAQ Delphi et les Cours et Tutoriels Delphi nuit gravement à notre code !

  16. #16
    Expert éminent sénior
    Avatar de Paul TOTH
    Homme Profil pro
    Freelance
    Inscrit en
    Novembre 2002
    Messages
    8 964
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Freelance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2002
    Messages : 8 964
    Points : 28 430
    Points
    28 430
    Par défaut
    Citation Envoyé par ShaiLeTroll Voir le message
    Très intéressant Gilbert Geyer !
    Je n'avais jamais pensé au = sur un ensemble, je pensais que seul l'opérateur in était disponible pour un Set
    C'est tellement simple set = [] ...

    En C++Builder, le == est facilement visible dans la doc, il fallait juste chercher plus en Delphi : Opérateurs d'ensembles Expressions (Delphi)

    Il serait intéressant de voir le code ASM et de comprendre ce que cela donne en pseudo-Delphi
    je viens d'ajouter les SET et SET OF à FlashPascal, je peux donc t'en parler


    en fait le SET ne fait que définir des constantes numériques, le SET OF et un champ de bits de type "1 shl X"

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
     
    type
      TNombre = (un = 1, deux, trois); // 1, 2, 3
      TNombres  = set of TNombre; // 0..7
    var
      nb: TNombres;
    begin
      nb := []; // nb = 0
      nb := [un]; // nb = 1 << 1;
      Include(nb, deux) // nb += 1 << 2;
      nb := nb + [trois]; // nb += 1 << 3;
      nb := nb - [un]; // nb &= !(1 << 1);
      nb := nb * [un, trois]; // nb &= (1 << 1) | (1 << 3);
      if deux in nb then //  if (nb & (1 << 2))
    end;
    Developpez.com: Mes articles, forum FlashPascal
    Entreprise: Execute SARL
    Le Store Excute Store

  17. #17
    Modérateur

    Homme Profil pro
    Ingénieur retraité
    Inscrit en
    Octobre 2005
    Messages
    2 396
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur retraité

    Informations forums :
    Inscription : Octobre 2005
    Messages : 2 396
    Points : 3 263
    Points
    3 263
    Par défaut
    Bonjour,

    ShaiLeTroll :
    Je n'avais jamais pensé au = sur un ensemble, je pensais que seul l'opérateur in était disponible pour un Set
    Bin, ça arrive qu'on oublie des choses simples. Et dans cette discussion : http://www.developpez.net/forums/d78...text-richedit/
    nous, on n'a même pas pensé à utiliser un Set Of Chars au point de créer une vingtaine de routines SupprChar(const S : string; CharSuppr : Char) pour deleter un seul Char à chaque appel.

    A+.
    N'oubliez pas de consulter les FAQ Delphi et les cours et tutoriels Delphi

  18. #18
    Modérateur

    Homme Profil pro
    Ingénieur retraité
    Inscrit en
    Octobre 2005
    Messages
    2 396
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur retraité

    Informations forums :
    Inscription : Octobre 2005
    Messages : 2 396
    Points : 3 263
    Points
    3 263
    Par défaut
    Re-bonjour,

    Paul TOTH :
    je viens d'ajouter les SET et SET OF à FlashPascal, je peux donc t'en parler
    Pour le cas où cela t'intéresserait d'ajouter des SET OF de plus de 256 éléments je viens de me souvenir que Sjrd a créé le "BigSet" téléchargeable dans cette discussion :http://www.developpez.net/forums/d41...ghlight=BigSet
    Il s'était avéré hyper-speed.

    A+.
    N'oubliez pas de consulter les FAQ Delphi et les cours et tutoriels Delphi

  19. #19
    Expert éminent sénior
    Avatar de Paul TOTH
    Homme Profil pro
    Freelance
    Inscrit en
    Novembre 2002
    Messages
    8 964
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Freelance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2002
    Messages : 8 964
    Points : 28 430
    Points
    28 430
    Par défaut
    Citation Envoyé par Gilbert Geyer Voir le message
    Re-bonjour,

    Paul TOTH :
    Pour le cas où cela t'intéresserait d'ajouter des SET OF de plus de 256 éléments je viens de me souvenir que Sjrd a créé le "BigSet" téléchargeable dans cette discussion :http://www.developpez.net/forums/d41...ghlight=BigSet
    Il s'était avéré hyper-speed.

    A+.
    256 ? ça me parait beaucoup pour un tableau de bits, 64 c'est déjà bien

    en fait Delphi utilise un tableau d'octets, donc il va au delà de mes 64bits...

    extrait de l'unité System de XE2 (version Pascal :{$IFNDEF CPUX86})
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    procedure _SetElem(var Dest {:Set}; Elem, Size: Integer);
    var
      P: PByte;
      I: Integer;
    begin
      P := @Dest;
      for I := 0 to Size - 1 do
        P[I] := 0;
      if (Elem >= 0) and ((Elem div 8) < Size) then
        P[Elem div 8] := 1 shl (Elem mod 8);
    end;
    code qui fonctionnerait avec plus de 256 éléments si je ne m'abuse.
    Developpez.com: Mes articles, forum FlashPascal
    Entreprise: Execute SARL
    Le Store Excute Store

  20. #20
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 447
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 447
    Points : 24 849
    Points
    24 849
    Par défaut
    Citation Envoyé par tourlourou Voir le message
    Un ensemble est stocké sous la forme d'un champ de Bits ; la comparaison est donc triviale.
    Citation Envoyé par Paul TOTH Voir le message
    en fait le SET ne fait que définir des constantes numériques, le SET OF et un champ de bits de type "1 shl X"
    J'ignore à qui s’adresse vos réponses mais c'est un peu de ce que l'on discute entre sgmsg et moi depuis quelques messages

    Et finalement, comme le fait remarquer Gilbert Geyer, je me suis pris la tête pour rien puisque set = [] fonctionne mieux que TSCALOON se basant justement sur le fait qu'un TSysCharSet c'est un champ de 256 bits = 32 octets = 8 Cardinal

    @Paul TOTH, Est-ce que set = [] invoque-t-il _SetEq ou une variante spéciale de l'ensemble vide ?
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

Discussions similaires

  1. remplacer une chaine ds un fichier rapidement !
    Par need2learn dans le forum Général Java
    Réponses: 7
    Dernier message: 05/08/2009, 19h30
  2. [Système] Remplacer une chaine par un lien hypertexte
    Par Bisûnûrs dans le forum Langage
    Réponses: 10
    Dernier message: 06/06/2007, 09h34
  3. Word ole et demande de remplacement de chaine
    Par dd16 dans le forum Langage
    Réponses: 6
    Dernier message: 24/09/2005, 12h56
  4. Réponses: 9
    Dernier message: 31/05/2005, 14h34
  5. Réponses: 4
    Dernier message: 16/04/2004, 16h31

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo