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

Lazarus Pascal Discussion :

Erreur delete(item) dans TStringList [Lazarus]


Sujet :

Lazarus Pascal

  1. #1
    Membre confirmé
    Profil pro
    Inscrit en
    Avril 2010
    Messages
    245
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2010
    Messages : 245
    Points : 534
    Points
    534
    Par défaut Erreur delete(item) dans TStringList
    Bonjour,

    Lazarus version: 1.5
    Lazarus svn revision: 49541 (SVN)
    Lazarus build date: 2015/07/13
    Lazarus was compiled for i386-win32 sous W7 64b
    Lazarus was compiled with fpc 3.1.1 (SVN).

    Dans une TStringList je veux supprimer les chaînes vides. Comme le programme provoque une erreur, j'ai fait un petit programme de test utilisant la même logique qui reproduit systématiquement la même erreur. Le voici dans le onclick d'un bouton.
    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
    procedure TForm1.Button1Click(Sender:TObject);
    var
      i,j:Integer;
      s:string;
      MaListe:TStringList;
    begin
      MaListe:=TStringList.Create;
      try
        MaListe.Add('une chaîne');
        MaListe.Add('une autre');
        repeat MaListe.Add('');
        until MaListe.Count=10;
        MaListe.Delete(MaListe.Count-1);//là on peut supprimer le dernier item
        i:=0;
        while i<MaListe.Count do
        begin
          while (MaListe[i]='') and (i<MaListe.Count) do
          begin
            s:='Index à supprimer : '+IntToStr(i);
            for j:=0 to MaListe.Count-1 do
              s:=s+LineEnding+'J= '+IntToStr(j)+' : '+MaListe[j];
            ShowMessage(s);//pour vérifier le contenu de MaListe
            MaListe.Delete(i);//erreur à l'index 2
          end;
          inc(i);
        end;
      finally
        MaListe.Free;
      end;
    end;
    Il lève systématiquement l'erreur "List index (2) out of bounds" lors du delete(2) lorsque cet item est devenu le dernier de la liste (affichée dans le showmessage pour vérifier).
    Ai-je fait une erreur quelque part ou est-ce une anomalie de cette version mise à jour sur les serveurs SVN?

    Merci.
    André

    Edit: Bon.. Sous Delphi 7 j'ai la même erreur, mais je ne comprend toujours pas pourquoi

  2. #2
    Membre éprouvé
    Homme Profil pro
    Chef de projets retraité
    Inscrit en
    Juillet 2011
    Messages
    420
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Cher (Centre)

    Informations professionnelles :
    Activité : Chef de projets retraité
    Secteur : Transports

    Informations forums :
    Inscription : Juillet 2011
    Messages : 420
    Points : 1 100
    Points
    1 100
    Par défaut
    Bonjour,

    En fait l'erreur ne se situe pas dans le
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
            MaListe.Delete(i);//erreur à l'index 2
    qui est bien exécute sans erreur mais dans le test de ta condition de boucle
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
          while (MaListe[i]='') and (i<MaListe.Count) do
    qui devrait être
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
          while (i<MaListe.Count) and (MaListe[i]='')  do
    Parce que tu essaie lorsque ton index est à 2 de lire maliste[2] alors qu'il n'y a plus que 2 chaînes dans ta liste avant de tester si tu dépasses les indices.

    NB. Cela ne marche que parce que le compilateur génère un code qui court circuite le second terme d'un AND si le premier terme est faux. Il fait de même pour un OR si le premier es vrai.

    Cordialement

  3. #3
    Membre confirmé
    Profil pro
    Inscrit en
    Avril 2010
    Messages
    245
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2010
    Messages : 245
    Points : 534
    Points
    534
    Par défaut
    Ok.
    Je n'ignorais pas la raison que tu expliques fort bien, mais je me suis polarisé sur l'erreur que le débogueur pointait sur la ligne du delete

    Un grand merci.
    André

  4. #4
    Membre chevronné

    Homme Profil pro
    au repos
    Inscrit en
    Février 2014
    Messages
    429
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : au repos

    Informations forums :
    Inscription : Février 2014
    Messages : 429
    Points : 1 884
    Points
    1 884
    Par défaut
    Bonsoir.

    Si je peux me permettre une petite précision...

    Pour faire une boucle pour supprimer des éléments, le plus simple est d'utiliser for... to..., mais en prenant garde de toujours commencer par la fin de la liste --> 0.

    Pour supprimer les chaines vides d'un StringList, il suffit de faire :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    procedure TForm1.Button1Click(Sender: TObject);
    var
      i: integer;
    begin
       for i:= MaListe.Count - 1 downto 0 do
           if MaListe[i] = '' then
              MaListe.Delete(i);
    end;
    Cordialement
    Thierry

  5. #5
    Membre confirmé
    Profil pro
    Inscrit en
    Avril 2010
    Messages
    245
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2010
    Messages : 245
    Points : 534
    Points
    534
    Par défaut
    Citation Envoyé par ThWilliam Voir le message
    Pour faire une boucle pour supprimer des éléments, le plus simple est d'utiliser for... to..., mais en prenant garde de toujours commencer par la fin de la liste --> 0.
    Je ne l'avais pas précisé dans le code ci-dessus qui n'avait pour objet que de rechercher la cause de l'anomalie, mais dans le programme réel, il faut également supprimer les doublons en gardant l'ordre à partir de l'index 0, ce qui contraint au balayage depuis le début.

    Bonne nuit.
    André

  6. #6
    Expert éminent sénior
    Avatar de Jipété
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    10 720
    Détails du profil
    Informations personnelles :
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations forums :
    Inscription : Juillet 2006
    Messages : 10 720
    Points : 15 106
    Points
    15 106
    Par défaut
    Bonjour,
    Citation Envoyé par alanglet Voir le message
    Je ne l'avais pas précisé dans le code ci-dessus qui n'avait pour objet que de rechercher la cause de l'anomalie, mais dans le programme réel, il faut également supprimer les doublons en gardant l'ordre à partir de l'index 0, ce qui contraint au balayage depuis le début.

    Bonne nuit.
    André
    ce qui contraint au balayage depuis le début --> c'est impossible en cas de suppression de lignes dans la liste.

    Parce que dans la ligne while i<MaListe.Count do la valeur de MaListe.Count a été mise dans une variable temporaire cachée à l'entrée de la boucle qui n'est plus réévaluée pendant les itérations.
    Supposons une liste de 10 entrées, MaListe.Count vaut 10, et la variable cachée aussi. Tu supprimes 5 entrées, MaListe.Count va valoir 5 mais la variable cachée reste à 10 et c'est elle qui est utilisée dans la boucle.
    Donc quand ton i va valoir 6, le programme va essayer d'accéder à MaListe[6] qui n'existe plus et paf !...

    Faut revoir ton code en prenant en compte cette contrainte, genre au lieu de supprimer dans MaListe, tu recopies ce que tu veux garder dans MaListeNew par exemple, et ensuite tu supprimes MaListe.
    Il a à vivre sa vie comme ça et il est mûr sur ce mur se creusant la tête : peutêtre qu'il peut être sûr, etc.
    Oui, je milite pour l'orthographe et le respect du trait d'union à l'impératif.
    Après avoir posté, relisez-vous ! Et en cas d'erreur ou d'oubli, il existe un bouton « Modifier », à utiliser sans modération
    On a des lois pour protéger les remboursements aux faiseurs d’argent. On n’en a pas pour empêcher un être humain de mourir de misère.
    Mes 2 cts,
    --
    jp

  7. #7
    Membre confirmé
    Profil pro
    Inscrit en
    Avril 2010
    Messages
    245
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2010
    Messages : 245
    Points : 534
    Points
    534
    Par défaut
    Bonjour,
    Citation Envoyé par Jipété Voir le message
    Parce que dans la ligne while i<MaListe.Count do la valeur de MaListe.Count a été mise dans une variable temporaire cachée à l'entrée de la boucle qui n'est plus réévaluée pendant les itérations.
    Pour m'en assurer, je viens de consulter mon vieux bouquin de Wirth datant de 84
    Dans l'instruction FOR, la valeur finale n'est évaluée qu'une seule fois au départ, mais pour l'instruction WHILE, l'expression qui commande la répétition est évaluée avant chaque répétition.
    Dans l'exemple ci-dessus, Count est bien réévalué à chaque fois, raison pour laquelle l'utilisation de FOR serait plus rapide.

    André

  8. #8
    Expert éminent sénior
    Avatar de Jipété
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    10 720
    Détails du profil
    Informations personnelles :
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations forums :
    Inscription : Juillet 2006
    Messages : 10 720
    Points : 15 106
    Points
    15 106
    Par défaut
    Citation Envoyé par alanglet Voir le message
    Dans l'exemple ci-dessus, Count est bien réévalué à chaque fois, raison pour laquelle l'utilisation de FOR serait plus rapide.
    Ah. Si tu es sûr de ton coup, j'ai peut-être bien dit une bêtise (ça m'arrive, avec cette chaleur [rhôôô l'excuse bidon ]) avec le while. Cependant avec le for ça foire :
    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
    procedure TForm1.Button1Click(Sender: TObject);
    var
      i: integer;
      st:TStringList;
    begin
      st:=TStringList.Create;
      // remplissage
      for i := 0 to 9 do st.Add(intToStr(i));
      // vidage
      //  for i := 0 to st.Count-1 do // crash !
      for i := st.Count-1 downto 0 do // OK
        // uniquement les pairs
        if (StrToInt(st[i]) mod 2 = 0) then st.Delete(i);
      // affichage
      memo1.Lines := st;
      memo1.Lines.Add(IntToStr(st.Count)); // 5
      st.Free;
    end;
    EDIT : et avec le while ça fonctionne, tu as raison !
    J'aurai encore appris quelque chose aujourd'hui ! ! !
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
      // vidage
      i := 0;
      while i < st.Count do
      begin
        // uniquement les pairs
        if (StrToInt(st[i]) mod 2 = 0) then st.Delete(i);
        inc (i);
      end;
      // affichage
      memo1.Lines := st;
      memo1.Lines.Add(IntToStr(st.Count)); // 5
      st.Free;
    /EDIT
    Il a à vivre sa vie comme ça et il est mûr sur ce mur se creusant la tête : peutêtre qu'il peut être sûr, etc.
    Oui, je milite pour l'orthographe et le respect du trait d'union à l'impératif.
    Après avoir posté, relisez-vous ! Et en cas d'erreur ou d'oubli, il existe un bouton « Modifier », à utiliser sans modération
    On a des lois pour protéger les remboursements aux faiseurs d’argent. On n’en a pas pour empêcher un être humain de mourir de misère.
    Mes 2 cts,
    --
    jp

  9. #9
    Expert confirmé
    Avatar de BeanzMaster
    Homme Profil pro
    Amateur Passionné
    Inscrit en
    Septembre 2015
    Messages
    1 899
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Amateur Passionné
    Secteur : Tourisme - Loisirs

    Informations forums :
    Inscription : Septembre 2015
    Messages : 1 899
    Points : 4 346
    Points
    4 346
    Billets dans le blog
    2
    Par défaut
    Salut à tous,

    Je viens de redécouvrir Lazarus et me balade sur ce forum pour me rappeler quelques techniques.

    Dans ton exemple, c'est ton 2eme while qui te provoque une erreur car c'est une boucle infinie.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    while (MaListe[i]='') and (i<MaListe.Count) do
          begin
            s:='Index à supprimer : '+IntToStr(i);
            for j:=0 to MaListe.Count-1 do
              s:=s+LineEnding+'J= '+IntToStr(j)+' : '+MaListe[j];
            ShowMessage(s);//pour vérifier le contenu de MaListe
            MaListe.Delete(i);//erreur à l'index 2
          end
    ;

    Ici tu n'incrémentes pas ton compteur "i" ; du coup tu as toujours le même index, ce qui te provoque une boucle infinie à cause du "(i<MaListe.Count)" et essaye donc d'accéder à la chaîne que tu viens de supprimer avec
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     MaListe.Delete(i);//erreur à l'index 2
    d'où cette erreur à l'index 2.

    Cordialement, à bientôt.
    • "L'Homme devrait mettre autant d'ardeur à simplifier sa vie qu'il met à la compliquer" - Henri Bergson
    • "Bien des livres auraient été plus clairs s'ils n'avaient pas voulu être si clairs" - Emmanuel Kant
    • "La simplicité est la sophistication suprême" - Léonard De Vinci
    • "Ce qui est facile à comprendre ou à faire pour toi, ne l'est pas forcément pour l'autre." - Mon pèrei

    Mes projets sur Github - Blog - Site DVP

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Erreur de compilation dans un environnement itemize
    Par asphalte dans le forum Erreurs - Avertissements
    Réponses: 7
    Dernier message: 11/02/2012, 09h50
  2. Auto-delete items dans une List<>
    Par istace.emmanuel dans le forum C#
    Réponses: 8
    Dernier message: 21/03/2011, 08h22
  3. Desactiver ,Activer Ou Delete Un Item Dans Un MainMenu
    Par aliwassem dans le forum Delphi
    Réponses: 9
    Dernier message: 16/03/2007, 18h46
  4. [système] Comment ajouter un item dans le context menu de Windows ?
    Par ddmicrolog dans le forum API, COM et SDKs
    Réponses: 8
    Dernier message: 29/06/2005, 17h03
  5. Réponses: 2
    Dernier message: 17/08/2003, 20h07

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