2009-07-31 13 views
11

Mam plik dziennika ASCII z niektórych treści, które chciałbym wyodrębnić. Nigdy nie poświęcałem czasu na naukę Perla, ale sądzę, że jest to dobre narzędzie do tego zadania.Jak wyodrębnić linie między dwoma ogranicznikami linii w Perlu?

Plik jest skonstruowany tak:

... 
... some garbage 
... 
... garbage START 
what i want is 
on different 
lines 
END 
... 
... more garbage ... 
next one START 
more stuff I want, again 
spread 
through 
multiple lines 
END 
... 
more garbage 

Tak, szukam sposobu, aby wyodrębnić linii pomiędzy każdym START i END ogranicznika strun. Jak mogę to zrobić?

Do tej pory znalazłem tylko kilka przykładów, jak wydrukować linię z ciągiem START lub inne elementy dokumentacji, które są nieco związane z tym, czego szukam.

+0

Użyj globalnego dopasowania/g zamiast pozwolić jej zatrzymać się na linii terminatora. – Lazarus

+0

Ci chodziło/s? AFAIK/g to ** wiele ** dopasowania. –

+1

To jest duplikat pytania. Zobacz .... http://stackoverflow.com/questions/296366/how-can-i-extract-lines-of-text-from-a-file/296672#296672 – draegtun

Odpowiedz

22

Chcesz operator flip-flop (lepiej znany jako operator zakres) ..

#!/usr/bin/env perl 
use strict; 
use warnings; 

while (<>) { 
    if (/START/../END/) { 
    next if /START/ || /END/; 
    print; 
    } 
} 

Replace wezwanie do print z tym, co rzeczywiście chcesz zrobić (na przykład przesunąć linię do tablicy, edycja to, sformatuj to, cokolwiek). Jestem next -w przeszłości linii, które faktycznie mają START lub END, ale możesz nie chcieć tego zachowania. Zobacz this article w celu omówienia tego operatora i innych użytecznych zmiennych specjalnych Perla.

+0

Działa dla mnie! Ponieważ chcę wykluczyć linie z ogranicznikami, mogę potokować wyjście przez grep -v na przykład. BTW, w pierwszym wierszu po START, jak mogę usunąć pierwszy znak w linii? – jbatista

+3

+1 dla przerzutnika –

+1

Wersja z jednym linkiem: perl -ne 'wydrukuj, jeśli /START/../END/' –

1
while (<>) { 
    chomp;  # strip record separator 
    if(/END/) { $f=0;} 
    if (/START/) { 
     s/.*START//g; 
     $f=1; 
    } 
    print $_ ."\n" if $f; 
} 

spróbować napisać trochę kodu następnym razem runda

+0

Rozumiem, i napisałbym kod, gdybym już zaczął uczyć się Perla. Udało mi się przejść z awk i sed do tej pory. Ale i tak dziękuję za twoją radę. – jbatista

1

Po odpowiedzi Telemach, wszystko zaczęło wylewanie. To działa jako rozwiązanie, na które patrzę.

  1. Próbuję wyodrębnić linii rozdzielonych przez dwa ciągi (jedna z linii zakończonej „CINFILE =”; drugi, z linii zawierającej pojedynczą „#”) w oddzielnych liniach, z wyłączeniem linii ogranicznika . To mogę zrobić z rozwiązaniem Telemachusa.
  2. Pierwsza linia ma miejsce, które chcę usunąć. Włączam to także.
  3. Próbuję również wyodrębnić każdy zestaw linii do osobnych plików.

To działa dla mnie, chociaż kod można zaklasyfikować jako brzydki; to dlatego, że obecnie jestem praktycznie nowym użytkownikiem w Perl. W każdym razie tutaj:

#!/usr/bin/env perl 
use strict; 
use warnings; 

my $start='CINFILE=$'; 
my $stop='^#$'; 
my $filename; 
my $output; 
my $counter=1; 
my $found=0; 

while (<>) { 
    if (/$start/../$stop/) { 
    $filename=sprintf("boletim_%06d.log",$counter); 
    open($output,'>>'.$filename) or die $!; 
    next if /$start/ || /$stop/; 
    if($found == 0) { print $output (split(/ /))[1]; } 
    else { print $output $_; } 
    $found=1; 
    } else { if($found == 1) { close($output); $counter++; $found=0; } } 
} 

Mam nadzieję, że przyniesie to korzyści również innym. Pozdrawiam.

5

Od odpowiedzi perlfaq6 „s do How can I pull out lines between two patterns that are themselves on different lines?


Można użyć Perl nieco egzotyczne ..operator (udokumentowane w perlop):

perl -ne 'print if /START/ .. /END/' file1 file2 ... 

Jeśli chciał tekst, a nie linie, należy użyć

perl -0777 -ne 'print "$1\n" while /START(.*?)END/gs' file1 file2 ... 

Ale jeśli chcesz zagnieżdżone wystąpienia START przez koniec, będziemy działać przeciwko problem opisany w pytaniu w tej sekcji dotyczącym dopasowywania wyważonego tekstu.

Oto kolejny przykład wykorzystania ..:

while (<>) { 
    $in_header = 1 .. /^$/; 
    $in_body = /^$/ .. eof; 
# now choose between them 
} continue { 
    $. = 0 if eof; # fix $. 
} 
1

Nieźle za przyjście z "wirtualnym newcommer". Jedną rzeczą, którą możesz zrobić, jest umieszczenie "$ found = 1" wewnątrz bloku "if ($ found == 0)", aby nie wykonywać tego zadania za każdym razem między $ start i $ stop.

Inną rzeczą, która jest nieco brzydka, moim zdaniem, jest to, że otwierasz ten sam filehandler za każdym razem, gdy wchodzisz do bloku $ start/$ stop.

To pokazuje drogę, że:

#!/usr/bin/perl 

use strict; 
use warnings; 

my $start='CINFILE=$'; 
my $stop='^#$'; 
my $filename; 
my $output; 
my $counter=1; 
my $found=0; 

while (<>) { 

    # Find block of lines to extract               
    if(/$start/../$stop/) { 

     # Start of block                  
     if(/$start/) { 
      $filename=sprintf("boletim_%06d.log",$counter); 
      open($output,'>>'.$filename) or die $!; 
     } 
     # End of block                   
     elsif (/$end/) { 
      close($output); 
      $counter++; 
      $found = 0; 
     } 
     # Middle of block                  
     else{ 
      if($found == 0) { 
       print $output (split(/ /))[1]; 
       $found=1; 
      } 
      else { 
       print $output $_; 
      } 
     } 

    } 
    # Find block of lines to extract               

} 
+0

Dzięki. Teraz czuję, że powinienem marnować^H^H^H^H^Huse trochę czasu, aby właściwie nauczyć się Perla. Moje doświadczenie dotyczy C, C++ i Fortran, więc wydaje się znajome. – jbatista

+0

BTW, przyznaję, że nie miałem wiele do powiedzenia na temat otwarcia wielu plików, w tamtym czasie moją główną troską było uzyskanie czegoś, co zadziałało, nawet jeśli niezbyt dobrze. – jbatista

Powiązane problemy