frank: string in blöcke aufteilen

hallo,

ich muss einen string variabler länge in perl in blöcke fester länge aufteilen.
habe also z.b. einen string:

$string="123456789asdgfbbnf";

dieser soll aufgeteilt werden in z.b. blöcke der länge 4, so dass am ende ein array mit allen blöcken entsteht. das letzte element des arrays muss also nicht zwanglaeufig (so wie in diesem beispiel) auch eine laenge von 4 haben.
es gibt keine festen trennzeichen, einzig die laenge soll als trennparameter dienen.

habe es schon mit
  @bloecke=split /(.{4})/,$string;
probiert, aber das liefert auch leere elemente zurueck.

auch
  @bloecke=();
  $string=~s/(.{4})/push(@bloecke,$1)/ge;
liefert nicht das gewuenschte ergebnis, da dann das letzte element fehlt, wenn es nicht genau die laenge 4 hat.

man koennte natuerlich vorher die laenge des strings ermitteln und dann so oft teile aus dem string lesen (und diesen kuerzen), wie es sich eben durch 4 teilen laesst, aber das muss doch auch einfacher gehen.

vielleicht kann mir jemand helfen

  1. Hallo Frank,

    habe es schon mit
      @bloecke=split /(.{4})/,$string;
    probiert, aber das liefert auch leere elemente zurueck.

    nun weil Leerzeichen auch Zeichen.

    Wie wäre es hiermit:

      
      
    # deinem split Beispiel  
      
    foreach ( split /(.{4})/, $string ) {  
       next if /^\s+$/;  
       s/^\s+//;  
       s/\s+$//;  
       push @bloecke;  
    }  
      
    # oder mit substr  
      
    my $len = length($string)/4;  
      
    for (my $i = 0 ; $len < $i ; $i += 4) {  
       push @bloecke, substr($string,$i,4);  
    }  
    
    

    Die substr Variante habe ich nicht getestet, aber der Ansatz sollte
    stimmen.

    Greez,
    opi

    --
    Selfcode: ie:( fl:( br:^ va:) ls:] fo:) rl:( n4:? ss:| de:] ch:? mo:|
    1. Hallo Frank,

      entschuldige, aber mein substr Beispiel enthielt einige Fehler!
      Dieses Beispiel hier habe ich gerade ausprobiert und es funktioniert
      fabelhaft :-)

        
      #!/usr/bin/perl  
        
      use strict;  
      use warnings;  
        
      my $string  = "123456789012 ww "; # ww zum Testen der entfernten Leerzeichen  
      my $bloecke = [];  
      my $laenge  = length($string);  
        
      for (my $i = 0 ; $laenge > $i ; $i += 4) {  
         my $val = substr($string,$i,4);  
         $val =~ s/^\s+//;  
         $val =~ s/\s+$//;  
         push @$bloecke, $val;  
      }  
        
      foreach (@$bloecke) {  
         print "<$_>\n";  
      }  
      
      

      Greez,
      opi

      --
      Selfcode: ie:( fl:( br:^ va:) ls:] fo:) rl:( n4:? ss:| de:] ch:? mo:|
      1. Hallo opi,

        my $string  = "123456789012 ww "; # ww zum Testen der entfernten Leerzeichen

        my $bloecke = [];
        my $laenge  = length($string);
        for (my $i = 0 ; $laenge > $i ; $i += 4) {
           my $val = substr($string,$i,4);
           $val =~ s/^\s+//;
           $val =~ s/\s+$//;
           push @$bloecke, $val;
        }
        foreach (@$bloecke) {
           print "<$_>\n";
        }

          
        ich weiß zwar nich, was du da für komische Handstände und Flickflacks machst, aber effizienter geht es z.B. so:  
          
        ~~~perl
          
        #!/usr/bin/perl -T  
        use strict;  
        use warnings;  
          
        my $string = "123456789012 ww ";  
        # String in 4er Zeichenblöcke ohne Leerzeichen zerlegen  
        my @bloecke = grep { /^\S+$/ } split /(\S{1,4})/, $string;  
          
        # Ausgabe  
        print join ", ", @bloecke;  
        print "\n";  
        
        

        Gruß,
        Benne

        --
        ie:% fl:( br:> va:) ls:> fo:| rl:° ss:) de:[ js:| ch:| mo:} zu:)
        1. Hallo Benne,

          ich weiß zwar nich, was du da für komische Handstände und Flickflacks machst

          dafür habe ich meinen Grund.

          aber effizienter geht es z.B. so:

          Falsch. Wenn du schon von Effizient sprichst ...

            
          Benchmark::cmpthese(-1, {  
                  'substr'  =>  sub { for (my $i = 0 ; $laenge > $i ; $i += 4) {  
                                         my $val = substr($string,$i,4);  
                                         next if $val =~ /^(\s+|)$/;  
                                         push @$bloecke, $val;  
                                      }  
                                },  
                  'split'   =>  sub { @$bloecke = grep { /^\S+$/ } split /(\S{1,4})/, $string; }  
          });  
          
          

          Rate  split substr
          split   9135/s     --   -55%
          substr 20210/s   121%     --

          substr ist doppelt so schnell.

          Greez,
          opi

          --
          Selfcode: ie:( fl:( br:^ va:) ls:] fo:) rl:( n4:? ss:| de:] ch:? mo:|
          1. Hallo opi,

            aber effizienter geht es z.B. so:
            Falsch. Wenn du schon von Effizient sprichst ...

            Benchmark::cmpthese(-1, {
                    'substr'  =>  sub { for (my $i = 0 ; $laenge > $i ; $i += 4) {
                                           my $val = substr($string,$i,4);
                                           next if $val =~ /^(\s+|)$/;
                                           push @$bloecke, $val;
                                        }
                                  },
                    'split'   =>  sub { @$bloecke = grep { /^\S+$/ } split /(\S{1,4})/, $string; }
            });

            ich spreche nicht von Performanz.  
            Ausschnitt aus <http://de.wikipedia.org/wiki/Effizient>:  
            ...ist das Verhältnis eines in definierter Qualität vorgegebenen Nutzens zu dem Aufwand, der zur Erreichung des Nutzens nötig ist.  
            ...In der Informatik meint man mit Effizienz entweder die Leistung des implementierten Algorithmus oder Komplexität (bei Algorithmen)  
              
            Man kann vielleicht eine längere Debatte führen, welche Methode komplexer ist.  
            Meine Meinung ist, dass die generelle Verwendung von grep/split "perlisher" und übersichtlicher ist.  
              
            Gruß,  
            Benne
            
            -- 
            ie:% fl:( br:> va:) ls:> fo:| rl:° ss:) de:[ js:| ch:| mo:} zu:)
            
            1. Hallo Benne,

              Man kann vielleicht eine längere Debatte führen, welche Methode komplexer ist.
              Meine Meinung ist, dass die generelle Verwendung von grep/split "perlisher" und übersichtlicher ist.

              meinst du mit "perlisher" weil du schön alles in eine Zeile packst?

              Ich finde mein Beispiel weder komplex noch unübersichtlich.

              ...In der Informatik meint man mit Effizienz entweder die Leistung des implementierten Algorithmus oder Komplexität (bei Algorithmen)

              Performanz ist doch Leistung.

              Die Leistung von substr ist mehr als das doppelte.

              Greez,
              opi

              --
              Selfcode: ie:( fl:( br:^ va:) ls:] fo:) rl:( n4:? ss:| de:] ch:? mo:|
  2. Hallo frank,

    dein Ansatz mit

    @bloecke=split /(.{4})/,$string;

    ist doch schon sehr gut. Die leeren Elemente kannst du ganz einfach mit grep raus werfen:

    my @bloecke = grep {$_ ne ""} split(/(.{4})/,$string);

    Mehr Informationen zu grep: perldoc -f grep

    Schöne Grüße,

    Norbert Klein

    1. Hallo Nobert,

      my @bloecke = grep {$_ ne ""} split(/(.{4})/,$string);

      Der String könnte aber auch wie folgt ausschauen:

      " "
       "  "
       "   "
       "    "

      substr wäre zudem ein wenig schneller :-)

      Greez,
      opi

      --
      Selfcode: ie:( fl:( br:^ va:) ls:] fo:) rl:( n4:? ss:| de:] ch:? mo:|
      1. Hallo.

        my @bloecke = grep {$_ ne ""} split(/(.{4})/,$string);

        Der String könnte aber auch wie folgt ausschauen:

        " "
        "  "
        "   "
        "    "

        Und?

        Du weißt aber, dass mit leeren Elementen ("") was anderes gemeint ist, als mit Leerzeichen (" "), ja?

        Schöne Grüße,

        Norbert Klein

        1. Hallo,

          my @bloecke = grep {$_ ne ""} split(/(.{4})/,$string);

          Du weißt aber, dass mit leeren Elementen ("") was anderes gemeint ist, als mit Leerzeichen (" "), ja?

          Wenn du Elemente mit nur Leerzeichen haben möchtest... ok.

          Und falls $string leer sein sollte, dann verarbeitest du den String
          doch sicherlich nicht.

          Greez,
          opi

          --
          Selfcode: ie:( fl:( br:^ va:) ls:] fo:) rl:( n4:? ss:| de:] ch:? mo:|
        2. Hallo

          Du weißt aber, dass mit leeren Elementen ("") was anderes gemeint ist, als mit Leerzeichen (" "), ja?

          leider hat frank nicht genau definiert, was er meint. So können wir hier nur raten. Schade eigentlich.

          Gruß,
          Benne

          --
          ie:% fl:( br:> va:) ls:> fo:| rl:° ss:) de:[ js:| ch:| mo:} zu:)
          1. also, ich meinte tatsaechlich leere elemente.
            leerzeichen im string sind nicht das problem, da ich diese auch in den bloecken haben muss. wie bereits gesagt, ist der inhalt des urspruenglichen strings vollkommen egal (also auch leerzeichen).
            das problem beim split ist, dass es ja "paarweise" ergebnisse liefert. falls der split also nur einen treffer hat (und der string genauso lang ist wie die blocklaenge), liefert er zwei ergebniselemente und eines ist eben leer. gleiches gilt fuer strings mit vielfachen der blocklaenge.

            ich muss auf diese art zwei strings unbekannten inhalts und unbekannter laenge blockweise vergleichen und da ist es nicht so toll, wenn ich leere elemente einfach ignoriere bzw. rauswerfe.

            die momentan beste loesung scheint mir die substr methode zu sein. ist auch relativ schnell.

            vielen dank

          2. Hallo Benne.

            leider hat frank nicht genau definiert, was er meint. So können wir hier nur raten. Schade eigentlich.

            Wenn man mit split eine Zeichenkette auseinander nimmt, und die Trennzeichen mitnehmen möchte, macht man das bekanntlich so:

              
            my @array = split(/(TRENNZEICHEN)/,$string);  
            
            

            Das Ergebnis wären folglich die Trennzeichen, sowie all die Teile der Zeichenkette, die keine Trennzeichen sind. Beispiel:

              
            my $string = 'The quick brown fox jumps over the lazy dog.';  
            my @array = split(/(\W)/,$string);  
            print "'".join("'\n'",@array)."'";  
            
            

            Die Ausgabe würde wie folgt aussehen:

            'The'
            ' '
            'quick'
            ' '
            'brown'
            ' '
            'fox'
            ' '
            'jumps'
            ' '
            'over'
            ' '
            'the'
            ' '
            'lazy'
            ' '
            'dog'
            '.'

            Wenn die Zeichenkette allerdings nur aus Trennzeichen besteht, wie in dem Fall, den frank beschrieben hat, sieht das Ganze so aus:

              
            my $string = 'The quick brown fox jumps over the lazy dog.';  
            my @array = split(/(\w+\W)/,$string);  
            print "'".join("'\n'",@array)."'";  
            
            

            Die Ausgabe wäre hierbei:

            ''
            'The '
            ''
            'quick '
            ''
            'brown '
            ''
            'fox '
            ''
            'jumps '
            ''
            'over '
            ''
            'the '
            ''
            'lazy '
            ''
            'dog.'

            Und wie wir sehen, entstehen dabei sehr viele leere Elemente, da sich zwischen den Trennzeichen nunmal keine weiteren Zeichen befinden. Und um eben diese leeren Elemente geht es.

            Benutzt man dann grep, um die leeren Elemente zu entfernen...

              
            my $string = 'The quick brown fox jumps over the lazy dog.';  
            my @array = grep {$_ ne ""} split(/(\w+\W)/,$string);  
            print "'".join("'\n'",@array)."'";  
            
            

            erhält man die gewünschte Ausgabe:

            'The '
            'quick '
            'brown '
            'fox '
            'jumps '
            'over '
            'the '
            'lazy '
            'dog.'

            Und ich vermute, dass frank mit den leeren Elementen genau das gemeint hat.

            Schöne Grüße,

            Norbert Klein