2009-10-10 8 views
0

J'ai creusé mon cerveau en essayant de trouver une solution, mais en vain. Toute orientation serait appréciée.Comment puis-je imprimer toutes les lignes entre les lignes vides précédente et suivante lorsqu'une correspondance est trouvée?

_data_ 
mascot 
friend 
ocean 
\n 
parsimon 
**QUERY** 
apple 
\n 
jujube 
\n 
apricot 
maple 
**QUERY** 
rose 
mahonia 
\n 

.... Étant donné le mot-clé de recherche est QUERY, il serait sortie:

parsimon 
**QUERY** 
apple 

apricot 
maple 
**QUERY** 
rose 
mahonia 

j'ai écrit un code qui ne fonctionne pas comme je le voudrais:

#!/usr/bin/perl 

use strict; 
use warnings; 

open my $fh, '<', 'FILE' or die "Cannot open: $!"; 
my @file = <$fh>; 
close $fh; 

for (0 .. $#file) { # read from the first line to the last 
    if($file[$_] =~ /QUERY/){ # if the contents of a particular line matches the query pattern 
     my $start = $_-- until $file[$_--] =~ /^$/; #check the previous line for an empty line. continue until success. store the index of the empty line to $start. 
     my $end = $_++ until $file[$_++] =~ /^$/; #check the next line for an empty line. continue until sucess. store the index of the empty line to $end. 

print "\n @file[$start..$end]"; #print all lines between the stored indexes 
} 
} 

J'ai également essayé quelque chose comme ça, mais il y avait une erreur syntaxique:

if($file[$_] =~ /QUERY/){ 
     my $start = $_-4 if $file[$_-4] =~ /^$/; 
     continue my $start = $_-3 if $file[$_-3]=~/^$/; 
    ------ 
my $end = $_+4 until $file[$_+4] =~ /^$/; 
..... 

print "\n @file[$start..$end]"; 
} 
..... 

Il semble que la seule bonne chose que j'ai jusqu'à présent réussi à atteindre est que je peux imprimer tout entre les lignes correspondantes et suivant les lignes vides en utilisant le code suivant:

for (0 .. $#file) { 
    if($file[$_+1] =~ /QUERY/) { 
    print $file[$_] until $file[$_++]=~/^$/; 

Quelqu'un peut-il me diriger dans le droit direction? Merci!

Mike

Modifier

Je pense que la solution de brian d foy à mon problème est le meilleur. Au mieux, je veux dire le plus efficace. Mais la solution de Jeff est la plus utile et je profite beaucoup de ses explications détaillées ligne par ligne et quoi de mieux, en utilisant son code, avec seulement quelques réglages, je peux faire autre chose, par exemple, imprimer toutes les lignes entre les lignes commençant par un chiffre lorsqu'un motif est trouvé. Et le code de Kinopiko est le code que j'espérais pouvoir écrire.

+1

Pourquoi? Votre sortie d'échantillon semble incompatible avec ce que vous cherchez réellement. –

+0

Je suppose qu'il y avait un format. jujube ne sera pas imprimé car il y a une ligne vide au dessus et en dessous. – Mike

+0

Désolé typo. Je veux dire qu'il y avait une erreur de format d'affichage. – Mike

Répondre

7

Wow, vous aimez vraiment faire beaucoup de travail dans ces réponses. Rappelez-vous, dans le traitement de texte, Perl rend les choses faciles faciles (et les choses difficiles possibles). Si vous faites beaucoup de travail pour quelque chose qui est facile à expliquer, vous manquez probablement la voie facile.:)

Redéfinissez simplement une ligne pour en faire un paragraphe et imprimez les paragraphes correspondants au fur et à mesure que vous les lisez. Vous pouvez modifier l'idée de Perl d'une ligne en définissant le séparateur d'enregistrement d'entrée, $/, comme étant la fin de ligne souhaitée. Lorsque vous utilisez l'opérateur d'entrée de ligne, vous récupérez tout ce qui est compris dans $/. Voir perlvar pour les détails sur Perl variables spéciales:

#!perl 

{ 
    local $/ = "\n\n"; 

    while(my $group = <DATA>) { 
     print $group if $group =~ /\Q**QUERY**/; 
    } 
} 


__DATA__ 
mascot 
friend 
ocean 

parsimon 
**QUERY** 
apple 

jujube 

apricot 
maple 
**QUERY** 
rose 
mahonia 

ghostdog74 posté sa version à un paquebot, que je légèrement modifié:

perl -ne "$/=qq(\n\n); print if /\Q**QUERY**/" fileA fileB ... 

perl dispose d'un commutateur de ligne de commande spéciale pour cela, cependant. Vous définissez le séparateur d'enregistrement d'entrée avec -0, et si vous définissez à 0, cela signifie que vous configurez pour utiliser le mode paragraphe:

perl -00 -ne "print if /\Q**QUERY**/" fileA fileB ... 

Le perlrun vous montre toutes les choses astucieuses que vous pouvez faire sur le ligne de commande. Pourquoi ne pas imprimer "jujube"?

+0

Testé correct. Il semble que ce soit un autre code intelligent! Vraiment impressionné. Et merci beaucoup d'avoir ouvert mes yeux. – Mike

+0

Est-ce que c'est la première ligne "local $/=" \ n \ n ";" met tout entre les lignes vides dans un groupe? – Mike

+1

Oui. Le $/est le séparateur d'enregistrement d'entrée. Tout ce qui est devient la chose qui sépare les lignes. L'astuce consiste à ne pas penser aux lignes telles que vous les voyez, mais à la façon dont un ordinateur peut les voir. :) –

1

Il y a quelques "hurleurs" ici.

  • Vous modifiez $ _ dans une boucle qui utilise $ _ comme variable de boucle.
  • Vous êtes doublement décrémenté et doublement incrémenté $ _.
  • Vous ne semblez pas avoir affaire au haut et au bas du fichier.

Cela semble fonctionner:

mon @file = < DONNÉES >

for (0 .. $#file) { 
    if ($file[$_] =~ /QUERY/){ 
my $start = $_; 
while ($start >= 0 && $file[$start] !~ /^$/) { 
    $start--; 
} 
     my $end = $_; 
while ($end <= $#file && $file[$end] !~ /^$/) { 
    $end++; 
} 
print "\[email protected][$start+1..$end-1]"; 
    } 
} 
__DATA__ 
mascot 
friend 
ocean 
parsimon 
QUERY 
apple 

jujube 
apricot 
maple 
QUERY 
rose 
mahonia 
+0

Les impressions de script "Résultats: 3 7 < parsimon QUERY pomme > Found: 9 15 < abricot érable QUERY rose mahonia >" – Mike

+0

Merci, mais peut-on se débarrasser de « Trouvé 3 7 "et" Trouvé: 9 15 "? – Mike

+0

Et merci d'avoir signalé mes erreurs stupides. J'espère que je m'améliore. – Mike

1

Il en résulte la sortie que vous avez spécifié, en supposant que dire « si QUERY se trouve, tout retour de la ligne vide précédente à la suivante ". Cela échouera dans le dernier cas s'il n'y a pas de ligne vide à la fin du fichier, mais vous pouvez simplement vérifier le dernier candidat après la boucle while.

#!/usr/bin/perl 

use strict; 
use warnings; 

# Open the file 
open my $fh, '<', 'FILE' or die "Cannot open: $!"; 

# initialize the candidate to an empty string 
# This is good form, so we know that we are starting with an empty candidate. 
my $candidate = ''; 

# Go through each line of the file 
# The contents of the line will be placed in $_ 
while(<$fh>) { 

    # If the line is blank, check the candidate we have accumulated 
    # This is shorthand for 'if($_ =~ /^$/) {' 
    if(/^$/) { 

     # If the candidate contains your QUERY, print it out 
     # Alternately, you could add it to an array, etc 
    if($candidate =~ /\nQUERY\n/) { 
      print "$candidate\n"; 
     } 

     # We are done with the previous candidate, so clear it out 
     $candidate = ''; 
    } else { 

     # If it is not a blank line, concatenate it to your 
     # candidate. I.e. build up all of the lines between blanks 
     $candidate .= $_; 
    } 
} 

# This handles the final lines, if there is not a blank line 
# at the end of the file 
if($candidate =~ /\nQUERY\n/) { 
    print "$candidate\n"; 
} 
+0

+1 C'est mieux que ma réponse. J'ai essayé d'adapter la méthode utilisée par Mike pour que cela fonctionne, mais vous l'avez fait mieux et plus simplement. –

+0

Mais ce code cesse de s'exécuter après le premier match. Il imprime uniquement « pomme QUERY parsimo », pas « abricot érable ** QUERY ** rose mahonia » – Mike

+1

Si vous lisez, il est mon explication parce qu'il n'y a pas de ligne vide à la fin du fichier. Ajoutez la ligne vide ou ajoutez l'instruction if ($ candidate) ... après la boucle while –

2
# more file 
mascot 
friend 
ocean 

parsimon 
**QUERY** 
apple 

jujube 

apricot 
maple 
**QUERY** 
rose 
mahonia 

# perl -ne '$/ = "\n\n";print $_ if /QUERY/' file 
    parsimon 
    **QUERY** 
    apple 

    apricot 
    maple 
    **QUERY** 
    rose 
    mahonia 
Questions connexes