2010-12-15 106 views
2

Je suis un débutant en perl. J'ai un fichier texte avec un texte similaire à ci-dessous. J'ai besoin d'extraire VALEUR = "< VALEUR NÉCESSAIRE>". Dites pour SPINACH, je devrais obtenir SALAD seul. Comment utiliser perl regex pour obtenir la valeur. J'ai besoin d'analyser plusieurs lignes pour l'obtenir. à-dire entre chaque #ifonly --- #endifonlyanalyser plusieurs lignes dans l'expression régulière perl et extraire la valeur

$ cat check.txt

while (<$file>) 
{ 
    if (m/#ifonly .+ SPINACH .+ VALUE=(")([\w]*)(") .+ #endifonly/g) 
{ 
    my $chosen = $2; 
    } 
} 


#ifonly APPLE CARROT SPINACH 
VALUE="SALAD" REQUIRED="yes" 
QW RETEWRT OIOUR 
#endifonly 
#ifonly APPLE MANGO ORANGE CARROT 
VALUE="JUICE" REQUIRED="yes" 
as df fg 
#endifonly 

Répondre

5
use strict; 
use warnings; 
use 5.010; 

while (<DATA>) { 
    my $rc = /#ifonly .+ SPINACH/ .. (my ($value) = /VALUE="([^"]*)"/); 
    next unless $rc =~ /E0$/; 
    say $value; 
} 

__DATA__ 
#ifonly APPLE CARROT SPINACH 
VALUE="SALAD" REQUIRED="yes" 
QW RETEWRT OIOUR 
#endifonly 
#ifonly APPLE MANGO ORANGE CARROT 
VALUE="JUICE" REQUIRED="yes" 
as df fg 
#endifonly 

Ceci utilise une petite astuce décrite par brian d foy here. Comme le lien le décrit, il utilise le scalaire range operator/flipflop.

+0

Egalement, un peu plus court: next sauf si (/ # ifonly. + SPINACH/.. (mon ($ value) =/VALEUR = "([^"] *) "/)) = ~/E0 $ /; Mais franchement, ça casse mon indentation, donc je ne l'utiliserais pas.:) Il se passe aussi pas mal de choses là-bas, ce qui n'est peut-être pas la meilleure pour la maintenabilité – Hugmeir

+0

Assez sympa, et (encore une fois) le lien que vous avez posté m'a appris quelque chose, alors merci pour cela – canavanin

+0

@canavanin J'ai tous les liens, vous êtes tous les bienvenus - le Effective Perler est mon blog Perl préféré, donc c'est toujours un plaisir de diriger les gens là-bas. – Hugmeir

0

Vous pouvez lire le contenu du fichier dans une chaîne, puis la recherche pour le modèle dans la chaîne:

my $file;  
$file.=$_ while(<>);  
if($file =~ /#ifonly.+?\bSPINACH\b.+?VALUE="(\w*)".+?#endifonly/s) { 
     print $1; 
} 

Votre regex d'origine a besoin de quelques ajustements:

  • Vous devez faire vos quantificateurs non gourmand.
  • Utilisez le modificateur s pour faire . correspondre newline aussi bien.

Ideone Link

1

Si votre fichier est très grand (ou si vous voulez le lire ligne par ligne pour une autre raison), vous pouvez le faire comme suit:

#!/usr/bin/perl 

use strict; 
use warnings; 
use Getopt::Long; 

my ($file, $keyword); 

# now get command line options (see Usage note below) 
GetOptions(
      "f=s" => \$file, 
      "k=s" => \$keyword, 
     ); 

# if either the file or the keyword has not been provided, display a 
# help text and exit 
if (! $file || ! $keyword) { 
    print STDERR<<EOF; 

    Usage: script.pl -f filename -k keyword 

EOF 
    exit(1); 
} 

my $found;   # indicator that the keyword has been found 
my $returned_word; # will store the word you want to retrieve 

open FILE, "<$file" or die "Cannot open file '$file': $!"; 
while (<FILE>) { 
    if (/$keyword/) { 
     $found = 1; 
    } 

    # the following condition will be true between all lines that 
    # start with '#ifonly' or '#endifonly' - but only if the keyword 
    # has been found! 
    if (/^#ifonly/ .. /^#endifonly/ && $found) { 
     if (/VALUE="(\w+)"/) { 
     $returned_word = $1; 
     print "looking for $keyword --> found $returned_word\n"; 

     last; # if you want to get ALL values after the keyword 
       # remove the 'last' statement, as it makes the script 
       # exit the while loop 
     } 
    } 
} 
close FILE; 
0

Voici une autre réponse basée sur l'opérateur bascule:

use strict; 
use warnings; 
use 5.010; 

while (<$file>) 
{ 
    if ((/^#ifonly.*\bSPINACH\b/ .. /^#endifonly/) && 
     (my ($chosen) = /^VALUE="(\w+)"/)) 
    { 
    say $chosen; 
    } 
} 

Cette solution applique le second test pour tous les des lignes de la gamme. L'astuce @Hugmeir utilisée pour exclure les lignes de début et de fin n'est pas nécessaire car la regex "interne", /^VALUE="(\w+)"/, ne peut jamais les faire correspondre (j'ai ajouté l'ancre ^ à toutes les expressions rationnelles pour en être doublement sûre).

0

Ces deux lignes dans une réponse donnée il y a deux jours

my $file; 
$file.=$_ while(<>); 

ne sont pas très efficaces. Perl lira probablement le fichier en gros morceaux, divisera ces morceaux en lignes de texte pour le <> et ensuite le .= rejoindra ces lignes pour faire une grosse chaîne. Il serait plus efficace de slurp le fichier. Le style de base consiste à modifier \$ le séparateur d'enregistrement d'entrée.

undef $/; 
$file = <>; 

Le module File::Slurp; (voir perldoc File::Slurp) peut être encore mieux.