Dauna: Tie::file und splice

Servus!

ich bins mal wieder der schwierige fall von letztem mal ;-))
zur erinnerung ich hab euch mal den beitrag vom lezten mal rausgesucht hier ist er http://forum.de.selfhtml.org/archiv/2008/9/t176639/ (falls es vielleicht nützlich sein könnte)

Also mein problem ist dieses ich muss in meiner ich nenn es jetzt mal datenbank ständig einträge löschen da dacht ich mir ich schreib mir ein programm wo ich nur etwas eingeben muss und es automatisch die zeile löscht. Soweit so schlecht :-(

meine datei sieht so aus:
####################################################################
<username>Dauna</username><name>Nicole</name><alter>18</alter><sonstiges>perl</sonstiges>
<username>anderename</username><name>jemand anderes</name><alter>435</alter><sonstiges>hallo</sonstiges>
####################################################################
und so weiter (ohne die rauten #)

wenn ich jetzt nach einem bestimmten eintrag suche zum beispiel dauna wird es auch ordentlich angezeigt also ich bekomme folgende line

<username>Dauna</username><name>Nicole</name><alter>18</alter><sonstiges>perl</sonstiges>

jetzt möcht ich einfach nur die zeile löschen und mein code sieht so aus:

my $open = "liste.txt";
tie my @datei,'Tie::File',$open;
for (@datei) {
if ($_ =~ m/<username>Dauna</username>/) {
#hier wird die oben ernannte zeile richtig ausgegeben
#jetzt will ich einfach die zeile löschen das mach ich so:

splice @datei,$_,1;

}
}
untie @datei;

eigentlich müsste es doch jetzt die zeile löschen oder??
es kommt aber eine hübsche fehlermeldung die sieht so aus:

Argument "<username>Dauna</username><name>Nicole</na..." isn´t numeric in numeric lt <<> at C:/perl/lib/Tie/File
.pm line 439, <$fh> line 6;

was mach ich falsch?

helf doch nochmal der gebrechlichen frau hehe
vielen vielen vielen vielen dank sag ich schonmal :-)

  1. Eigentlich sollte das:

    splice @datei,$_,1;

    kombiniert mit dem:

    Argument "<username>Dauna</username><name>Nicole</na..." isn´t numeric ...

    und der Lektüre von perldoc klar werden lassen, was der Fehler ist. Du musst splice nicht den Inhalt der Zeile ($_), sondern den aktuellen Index übergeben.

    Das Problem ist, dass Dir der Index in einer for/foreach-Schleife nicht von Perl geliefert wird, Du musst also mitzählen. Das könnte dann so aussehen:

    my $i = 0;  
    foreach(@arr) {  
      splice @arr, $i, 1 if /$pattern/;  
      $i++;  
    }
    

    Siechfred

    --
    Obacht, hinter jedem noch so kleinen Busch könnte ein Indianer sitzen!
    1. Das Problem ist, dass Dir der Index in einer for/foreach-Schleife nicht von Perl geliefert wird, Du musst also mitzählen. Das könnte dann so aussehen:

      my $i = 0;

      foreach(@arr) {
        splice @arr, $i, 1 if /$pattern/;
        $i++;
      }

        
      Achtung bei diesem Code.  
      Es ist nicht empfehlenswert, den Array zu splicen, über den ich gerade iteriere.  
      Auch ist es elend langsam, sobald mehrere Einträge gelöscht werden müssen.  
        
      Ich lese nämlich gerade meinen eigenen perlcode, in welchem ich Leere Zeilen in einem Array bei Tie::File löschen will.  
      Ich habe das Problem hierbei wie folgt gelöst:  
        
      In dieser Aufgabe habe ich mehrere Zeilen zu löschen,  
      Das registrieren der Löschzeilen und das effektive Löschen sind hierbei verschiedene Vorgänge. Splice finden NICHT während dem Iterieren statt.  
        
      Ich habe das Script gekürzt.  
        
      ~~~perl
      my @mes; # Dieser Array soll bereinigt werden.  
      tie( @mes, 'Tie::File', $Files{messages} ) or  
           error('datei', "Sub Comtrash Tied $Files{messages} $!");  
      # ...  
      my $cnt = 0;    # Dieser Counter ist ganz wichtig, denn er soll die  
                      #Arrayelemente zählen und den Indes in @splicers eintragen  
      my @splicers;   # ein Hilfsarray  
        
      for( @mes ){  
        if(...){...}  
        if(...){  
            #...  
            # warum unshift statt push? siehe unten.  
            unshift( @splicers, $cnt );  
        }  
        # ...  
        $cnt++;  
      }  
      # jetzt die in @splice gespeicherten Zeilen aufräumen, von hinten!  
      foreach( @splicers ){  
        splice( @mes, $_, 1);  
      }  
      untie( @mes );  
      
      

      Gut daran ist, dass ich nicht über den Array splice, über welchen ich iteriere.
      Problematisch ist daran eventuell die Performance. Aber die ist bei splice immanent.
      Es hängt ganz davon ab, wie stark @splice populiert sind, und ob es sich um späte Elemente im Array handelt (gut), oder um frühe (schlecht).
      Weil in @splicers die Einträge absteigend vorliegen (29,16,12,9,3), habe ich den Vorzug, das splicing von hinten beginnt, und damit tendenziell schneller arbeitet.

      Gäbe es hier noch etwas zu optimieren?
      Ja ich sehe noch Potential
        splice( @mes, $_, 1);
      Wenn zwei Elemente aufeinanderfolgen wie in
      @splicers = (29,28,27,8,7,1)
      liesse sich der Umstand beschleunigen, dass 29,28,27 in einer einzigen Operation gespliced werden könnten.

      mfg Beat

      --
      Woran ich arbeite:
      X-Torah
         <°)))o><                      ><o(((°>o
      1. Es ist nicht empfehlenswert, den Array zu splicen, über den ich gerade iteriere.

        Diesen Hinweis habe ich schon des öfteren gelesen, aber noch keine für mich nachvollziehbare Begründung dafür. Könntest Du da mal was genaues liefern?

        Siechfred

        --
        Obacht, hinter jedem noch so kleinen Busch könnte ein Indianer sitzen!
        1. Es ist nicht empfehlenswert, den Array zu splicen, über den ich gerade iteriere.

          Diesen Hinweis habe ich schon des öfteren gelesen, aber noch keine für mich nachvollziehbare Begründung dafür. Könntest Du da mal was genaues liefern?

          Hier ist ein kleiner Testcase

            
          #!C:/Programme/Perl/bin/perl.exe -w  
          #  
          use strict;  
            
          BEGIN {  
           use CGI::Carp qw(carpout);  
           open(LOG, ">>error.txt")  or  die "Unable to append to error.txt: $!\n";  
           carpout(*LOG);  
          }  
            
          my @ar = (1..10);  
          my $cnt = 0;  
          foreach(@ar){  
            # zeige den Zustand vor dem Splicen.  
            print "vorher", $_,"-",$ar[$cnt], ' nachher';  
            splice(@ar,$cnt,1);  
            # zeige den Zustand nach dem Splicen.  
            print $_,"-",$ar[$cnt], "\n";  
            $cnt++;  
          }  
          print scalar(@ar), "\n";  
          # in diesem testcase sind immer noch 5 Elemente im array.  
          sleep(10);  
            
          exit;  
          
          

          Wie interpretierst du das Ergebnis?

          mfg Beat

          --
          Woran ich arbeite:
          X-Torah
          ><o(((°>       ><o(((°>
             <°)))o><                      ><o(((°>o
          1. Wie interpretierst du das Ergebnis?

            Klar, ich schmeiße Element n raus, damit ist das nächst Element nicht wie normalerweise n+1, sondern n+2. Hab's kapiert, danke für den Schubser.

            Siechfred

            --
            Obacht, hinter jedem noch so kleinen Busch könnte ein Indianer sitzen!
            1. Servus und vielen dank das klappt ja super!

              Es klappt zwar aber ich verstehs nicht ganz ;-)

              Es funktioniert doch folgender maßen:

              Datei wird geöffnet mit tie, datei wird durchlaufen und die änderungen in @splices gespeichert der counter gibt an wie oft in der datei das suchende stück enthalten ist und gibt es an @splices (wenn jetzt also 8 leerzeichen enthalten sind wird dank $cnt nicht nur eines gelöscht sondern 8 also ist $cnt = 8 versteh ich das richtig). Danach wird die schleife beendet und die @splices schleife gestartet in der "splicet" man und es wird in der datei gespeichert?

              Apropo mir scheint da noch etwas nicht klar zu sein wenn ich eine zeile lösche ist sie zwar weg aber die zeile existriert noch nun möcht ich diese zeile ganz löschen und eigentlich mach ich es doch richtig und in google find ich auch das gleiche aber es klappt einfach nicht hier ein codeschnipsel:

              for( @dat ){
              if($_ =~ m/^\s+/){
              unshift( @splicers, $cnt );
              }
              $cnt++;
              }
              foreach( @splicers ){
                splice( @dat, $_, 1);
              }

              das klappt nicht aber auch das klappt nicht

              #mit tie geöffnet
              for (@dat) {
              s/^\s+//;
              }

              Was mach ich jezt schon wieder falsch?
              Irgendwie lerne ich und lerne ich perl und weiss es aber wenn ich es anwende funktioniert nie was das macht keinen spaß mehr :-((

              vielen vielen dank!

              1. Servus und vielen dank das klappt ja super!

                Es klappt zwar aber ich verstehs nicht ganz ;-)

                Es funktioniert doch folgender maßen:

                Datei wird geöffnet mit tie, datei wird durchlaufen und die änderungen in @splices gespeichert

                Nein. Es wird $cnt in @splacers angefügt.
                $cnt enthält die indexnummer des gerade durchlaufenen Arrayelements.
                Diese indexnummer brauche ich in der zweiten foreach Schleife, um sie dann in splice() zu verwenden.

                der counter gibt an wie oft in der datei das suchende stück enthalten ist

                Nein, der Counter ist ein Schleifenzähler. Er wird unabhängig vom Erfolg oder Misserfolg hochgezählt. Dadurch ist $cnt immer identisch mit der Indexnummer des bearbeiteten filearray elements.

                und gibt es an @splices (wenn jetzt also 8 leerzeichen enthalten sind wird dank $cnt nicht nur eines gelöscht sondern 8 also ist $cnt = 8 versteh ich das richtig).

                Nein für äcnt ist irrelevant, was im Arrayelement drinsteht.
                Wenn in der IfPrüfung die eine von dir zu bestimmende Bedingung erfüllt wird, dass wird der Index des gerade bearbeiteten Arrayelements zu @splicers hinzugefügt.

                Danach wird die schleife beendet und die @splices schleife gestartet in der "splicet" man und es wird in der datei gespeichert?

                Apropo mir scheint da noch etwas nicht klar zu sein wenn ich eine zeile lösche ist sie zwar weg aber die zeile existriert noch nun möcht ich diese zeile ganz löschen und eigentlich mach ich es doch richtig und in google find ich auch das gleiche aber es klappt einfach nicht hier ein codeschnipsel:

                for( @dat ){
                  if($_ =~ m/^\s+/){
                    unshift( @splicers, $cnt );
                  }
                  $cnt++;
                }
                foreach( @splicers ){
                  splice( @dat, $_, 1);
                }

                das klappt nicht aber auch das klappt nicht

                Was meinst, du mit _es_ klappt nicht. Sofern deine IF bedingung erfüllt wird, wird genau das gemacht. Das Element wird aus @dat entfernt, und damit ist die Zeile bei untie dann gänzlich getilgt.

                #mit tie geöffnet
                for (@dat) {
                s/^\s+//;
                }

                Du suchst nach Zeilen die mindestens ein Whitespace Zeichen enthalten. Das ist nicht das gleiche wie _leere zeile_

                Eine Leere Zeile besteht aus einer Zeile, die nicht einmal Whitespace beinhaltet. Das solltest du im Quantifier berücksichtigen.

                Was mach ich jezt schon wieder falsch?
                Irgendwie lerne ich und lerne ich perl und weiss es aber wenn ich es anwende funktioniert nie was das macht keinen spaß mehr :-((

                mfg Beat

                --
                Woran ich arbeite:
                X-Torah
                   <°)))o><                      ><o(((°>o