Domanda

Ho un file di registro ASCII con alcuni contenuti che vorrei estrarre. Non ho mai avuto il tempo di imparare correttamente Perl, ma immagino che questo sia un buon strumento per questo compito.

Il file è strutturato in questo modo:

... 
... 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

Quindi, sto cercando un modo per estrarre le linee tra ciascuna stringa delimitatore START e END . Come posso farlo?

Finora ho trovato solo alcuni esempi su come stampare una riga con la stringa START o altri elementi della documentazione che sono in qualche modo correlati a ciò che sto cercando.

È stato utile?

Soluzione

Si desidera l'operatore flip-flop (meglio noto come operatore di intervallo) ..

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

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

Sostituisci la chiamata a print con qualunque cosa tu voglia realmente fare (ad esempio, spingere la linea in un array, modificarla, formattarla, qualunque cosa). Sto successivo -stando oltre le righe che in realtà hanno START o END , ma potresti non voler quel comportamento. Vedi questo articolo per una discussione di questo operatore e di altri utili variabili speciali Perl.

Altri suggerimenti

Dalla perlfaq6 la risposta di Come posso estrarre linee tra due motivi che si trovano su linee diverse?


Puoi usare l'operatore un po 'esotico di Perl (documentato in perlop):

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

Se volessi del testo e non delle righe, dovresti usare

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

Ma se desideri occorrenze nidificate di START tramite END, ti imbatterai nel problema descritto nella domanda in questa sezione sulla corrispondenza del testo bilanciato.

Ecco un altro esempio dell'uso di ..:

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

Come posso ottenere più righe dopo una riga corrispondente in Perl?

Come va? In quello, la stringa END è $ ^, puoi cambiarla nella tua stringa END.

Sono anche un principiante, ma le soluzioni lì forniscono alcuni metodi ... fammi sapere più specificamente ciò che vuoi che differisce dal link sopra.

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

prova a scrivere del codice la prossima volta

."\n" if $f; }

prova a scrivere del codice la prossima volta

Dopo la risposta di Telemaco, le cose hanno iniziato a riversarsi. Funziona come la soluzione che sto cercando dopo tutto.

  1. Sto cercando di estrarre le linee delimitate da due stringhe (una, con una linea che termina con " CINFILE = " ;; altro, con una linea contenente un singolo " # ") in righe separate, escluso il delimitatore Linee. Questo posso farlo con la soluzione di Telemaco.
  2. La prima riga ha uno spazio che voglio rimuovere. Lo sto anche includendo.
  3. Sto anche cercando di estrarre ogni set di righe in file separati.

Questo funziona per me, anche se il codice può essere classificato come brutto; questo perché al momento sono praticamente un nuovo arrivato in Perl. Comunque qui va:

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

my $start='CINFILE=

Spero che vada a beneficio anche degli altri. Cin cin.

; my $stop='^#

Spero che vada a beneficio anche degli altri. Cin cin.

; 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

Dopo la risposta di Telemaco, le cose hanno iniziato a riversarsi. Funziona come la soluzione che sto cercando dopo tutto.

  1. Sto cercando di estrarre le linee delimitate da due stringhe (una, con una linea che termina con " CINFILE = " ;; altro, con una linea contenente un singolo " # ") in righe separate, escluso il delimitatore Linee. Questo posso farlo con la soluzione di Telemaco.
  2. La prima riga ha uno spazio che voglio rimuovere. Lo sto anche includendo.
  3. Sto anche cercando di estrarre ogni set di righe in file separati.

Questo funziona per me, anche se il codice può essere classificato come brutto; questo perché al momento sono praticamente un nuovo arrivato in Perl. Comunque qui va:

<*>

Spero che vada a beneficio anche degli altri. Cin cin.

; } $found=1; } else { if($found == 1) { close($output); $counter++; $found=0; } } }

Spero che vada a beneficio anche degli altri. Cin cin.

Non male per venire da un "nuovo arrivato virtuale". Una cosa che potresti fare è mettere " $ found = 1 " all'interno di " if ($ found == 0) " bloccare in modo da non fare quel compito ogni volta tra $ start e $ stop.

Un'altra cosa che è un po 'brutta, secondo me, è che apri lo stesso gestore di file ogni volta che entri nel blocco $ start / $ stop.

Questo mostra un modo per aggirare questo:

#!/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 

Non male per venire da un "nuovo arrivato virtuale". Una cosa che potresti fare è mettere " $ found = 1 " all'interno di " if ($ found == 0) " bloccare in modo da non fare quel compito ogni volta tra $ start e $ stop.

Un'altra cosa che è un po 'brutta, secondo me, è che apri lo stesso gestore di file ogni volta che entri nel blocco $ start / $ stop.

Questo mostra un modo per aggirare questo:

<*>; } } } # Find block of lines to extract }
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top