Comment extraire des lignes entre deux délimiteurs de ligne dans Perl?
Question
J'ai un fichier journal ASCII avec du contenu que je voudrais extraire. Je n'ai jamais pris le temps d'apprendre correctement Perl, mais je pense que c'est un bon outil pour cette tâche.
Le fichier est structuré comme suit:
... ... 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
Je cherche donc un moyen d'extraire les lignes entre chaque chaîne de délimiteur START
et END
.
Comment puis-je faire cela?
Jusqu'à présent, je n'ai trouvé que quelques exemples sur la façon d'imprimer une ligne avec la chaîne START
, ou d'autres éléments de documentation quelque peu liés à ce que je recherche.
La solution
Vous voulez l'opérateur de la bascule (plus connu sous le nom d'opérateur de plage) ..
#!/usr/bin/env perl
use strict;
use warnings;
while (<>) {
if (/START/../END/) {
next if /START/ || /END/;
print;
}
}
Remplacez l'appel à print
par ce que vous voulez réellement faire (p. ex., placez la ligne dans un tableau, modifiez-le, formatez-le, etc.). Je passe à côté
au-delà des lignes comportant START
ou FIN
, mais vous ne voudrez peut-être pas ce comportement. Voir cet article pour une discussion de cet opérateur et d'autres variables spéciales Perl utiles.
Autres conseils
De la réponse de perlfaq6 à Comment puis-je tirer des lignes entre deux modèles qui sont eux-mêmes sur des lignes différentes?
Vous pouvez utiliser l'opérateur un peu exotique de Perl (documenté dans perlop):
perl -ne 'print if /START/ .. /END/' file1 file2 ...
Si vous vouliez du texte et non des lignes, vous utiliseriez
perl -0777 -ne 'print "$1\n" while /START(.*?)END/gs' file1 file2 ...
Mais si vous souhaitez que des occurrences imbriquées de START à FIN soient rencontrées, vous vous heurterez au problème décrit dans la question de la correspondance dans cette section sur la correspondance du texte équilibré.
Voici un autre exemple d'utilisation ..:
while (<>) {
$in_header = 1 .. /^$/;
$in_body = /^$/ .. eof;
# now choose between them
} continue {
$. = 0 if eof; # fix $.
}
Comment puis-je en saisir plusieurs lignes après une ligne correspondante en Perl?
Comment ça va? Dans celui-ci, la chaîne END est $ ^, vous pouvez le changer pour votre chaîne END.
Je suis aussi un novice, mais les solutions proposées ici fournissent pas mal de méthodes ... faites-moi savoir plus précisément ce que vous voulez qui diffère du lien ci-dessus.
while (<>) {
chomp; # strip record separator
if(/END/) { $f=0;}
if (/START/) {
s/.*START//g;
$f=1;
}
print <*>
essayez d'écrire du code la prochaine fois
."\n" if $f;
}
essayez d'écrire du code la prochaine fois
Après la réponse de Télémaque, les choses ont commencé à couler. Cela fonctionne comme la solution que je cherche après tout.
- J'essaie d'extraire des lignes délimitées par deux chaînes (une avec une ligne se terminant par "CINFILE ="; une autre, avec une ligne contenant un seul "#") dans des lignes séparées, à l'exclusion du délimiteur. lignes. Je peux le faire avec la solution de Télémaque.
- La première ligne comporte un espace que je souhaite supprimer. Je l’inclus aussi.
- J'essaie également d'extraire chaque ensemble de lignes dans des fichiers séparés.
Cela fonctionne pour moi, bien que le code puisse être classé comme laid; C'est parce que je suis pratiquement un nouvel arrivant sur Perl. Quoi qu'il en soit, voici:
#!/usr/bin/env perl
use strict;
use warnings;
my $start='CINFILE=
J'espère que cela profite également aux autres.
Cheers.
;
my $stop='^#
J'espère que cela profite également aux autres.
Cheers.
;
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 Après la réponse de Télémaque, les choses ont commencé à couler. Cela fonctionne comme la solution que je cherche après tout.
- J'essaie d'extraire des lignes délimitées par deux chaînes (une avec une ligne se terminant par "CINFILE ="; une autre, avec une ligne contenant un seul "#") dans des lignes séparées, à l'exclusion du délimiteur. lignes. Je peux le faire avec la solution de Télémaque.
- La première ligne comporte un espace que je souhaite supprimer. Je l’inclus aussi.
- J'essaie également d'extraire chaque ensemble de lignes dans des fichiers séparés.
Cela fonctionne pour moi, bien que le code puisse être classé comme laid; C'est parce que je suis pratiquement un nouvel arrivant sur Perl. Quoi qu'il en soit, voici:
<*>
J'espère que cela profite également aux autres.
Cheers.
; }
$found=1;
} else { if($found == 1) { close($output); $counter++; $found=0; } }
}
J'espère que cela profite également aux autres. Cheers.
Pas si mal pour venir d'un "nouveau client virtuel". Une chose que vous pouvez faire est de mettre le " $ trouvé = 1 " à l’intérieur du " if ($ found == 0) " bloquer afin que vous ne fassiez pas cette affectation à chaque fois entre $ start et $ stop.
Une autre chose un peu moche, à mon avis, est que vous ouvrez le même gestionnaire de fichiers à chaque fois que vous entrez le bloc $ start / $ stop-block.
Cela montre un moyen de contourner ce problème:
#!/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 Pas si mal pour venir d'un "nouveau client virtuel". Une chose que vous pouvez faire est de mettre le " $ trouvé = 1 " à l’intérieur du " if ($ found == 0) " bloquer afin que vous ne fassiez pas cette affectation à chaque fois entre $ start et $ stop.
Une autre chose un peu moche, à mon avis, est que vous ouvrez le même gestionnaire de fichiers à chaque fois que vous entrez le bloc $ start / $ stop-block.
Cela montre un moyen de contourner ce problème:
<*>;
}
}
}
# Find block of lines to extract
}