2012-08-29 10 views
6

Quiero crear dinámicamente un hash %detail, sin usar una declaración eval. Este código funciona bien con una declaración eval, pero ¿hay alguna forma mejor de realizar esto sin usar eval?

my @input=('INFO: Vikram 32 2012','SAL: 12000$','ADDRESS: 54, junk, JUNK'); 

my %matching_hash= (
        qr/^INFO:\s*(\S+)\s+(\S+)\s+(\S+)/ =>['name','age','joining'], 
        qr/^SAL:\s*(\S+)/ => ['salary'], 
        qr/ADDRESS:\s*(.*)/ =>['address'] 
        ); 
my %detail; 
while(my ($regex, $array) = each(%matching_hash)) { 
    foreach (@input){ 
     if(/$regex/) { 
      for(my $i=0;$i<=$#$array; $i++) { 
       $j=$i+1; 
       eval '$detail{$array->[$i]} = $$j'; 
      } 
     } 
    } 
} 
use Data::Dumper; 

print Dumper(\%detail); 
++++++++++++++ 

$VAR1 = { 
      'name' => 'Vikram', 
      'address' => '54, junk, JUNK', 
      'age' => '32', 
      'joining' => '2012', 
      'salary' => '12000$' 
     }; 
+0

No creo que su declaración de evaluación funcione si 'use strict' (tuve que eliminarla de mi prueba para obtener el resultado deseado). –

Respuesta

5

Cambiar el bucle for:

for(my $i=0;$i<=$#$array; $i++) { 
    $j=$i+1; 
    eval '$detail{$array->[$i]} = $$j'; 
} 

por:

@detail{@{$array}} = ($_ =~ $regex); 
1

Puede utilizar las dos matrices @LAST_MATCH_START y @LAST_MATCH_END (ver perldoc perlvar) junto con substr en lugar de $1, $2.... Algo similar a

$detail{ $array->[$i] } = substr $_, $LAST_MATCH_START[$j], $LAST_MATCH_END[$j] - $LAST_MATCH_START[$j]; 
+0

De acuerdo, excepto por el 'inglés '. – hobbs

14

parte pertinente:

if(my @m = /$regex/) { 
    for(my $i=0;$i<=$#$array; $i++) { 
     $detail{$array->[$i]} = $m[$i];    
    } 
} 
2

Si puede utilizar última versión de Perl, ver a esta notación (?<name>...) en expresión regular perlre docs es más claro a continuación, utilizando $ 1, $ 2, $ 3, etc.

SCRIPT

use v5.14; 
use Data::Dumper; 

my @inputs = ('INFO: Vikram 32 2012', 'SAL: 12000$','ADDRESS: 54, junk, JUNK'); 

my %matching_hash= (
    qr/^INFO:\s*(?<name>\S+)\s+(?<age>\S+)\s+(?<joining>\S+)/ => [ 'name', 'age', 'joining' ], 
    qr/^SAL:\s*(?<salary>\S+)/        => [ 'salary' ], 
    qr/ADDRESS:\s*(?<address>.*)/        => [ 'address' ], 
); 

my %detail; 
while (my ($regex, $array) = each %matching_hash) { 

    INPUT: 
    foreach my $input (@inputs) { 

     next INPUT if not $input =~ m{$regex}; 

     for my $name (@$array) { 
      $detail{$name} = $+{$name}; 
     } 
    } 
} 

say Dumper(\%detail); 

SALIDA

$VAR1 = { 
      'name' => 'Vikram', 
      'address' => '54, junk, JUNK', 
      'age'  => '32', 
      'joining' => '2012', 
      'salary' => '12000$' 
     }; 
+2

No debería necesitar las matrices asignadas si va a usar coincidencias con nombre solo '@detail {keys% +} = values% + if $ input = ~ m {$ regex};' o incluso '% detail =% + if $ input = ~ m {$ regex}; '. Entonces podrías poner las expresiones regulares en una estructura que sea mucho más adecuada para la iteración, una lista/matriz. +1 por lo que hubiera sugerido. – Axeman

+0

'% detail =% + if $ input = ~ m {$ regex};' no funcionaría, descarta lo que ya está en '% detail'. –

1

Usando named capture groups, puede eliminar la necesidad de %matching_hash ser un hash Mientras que al mismo tiempo elimina la necesidad de usar las variables numéricas, o asigna el resultado de la coincidencia en una matriz. Esto se debe a que almacenará la información relevante en %+.

use 5.10.1; 

my @match = (
    qr'^INFO:\s*(?<name>\S+)\s+(?<age>\S+)\s+(?<joining>\S+)', 
    qr'^SAL:\s*(?<salary>\S+)', 
    qr'ADDRESS:\s*(?<address>.*)', 
); 

sub get_details{ 
    my %detail; 

    for my $input (@_) { 
    for my $match (@match){ 
     next unless $input =~ $match; 
     @detail{keys %+} = values %+; 
     last; 
    } 
    } 

    return \%detail; 
} 

use Data::Dumper; 
my @inputs = ('INFO: Vikram 32 2012', 'SAL: 12000$','ADDRESS: 54, junk, JUNK'); 
say Dumper get_details @inputs 

Se puede conseguir aún más sencillo si combinas qr 's en una sola.

use 5.10.1; 

my $match= qr" 
    ^INFO: \s* (?<name>\S+) \s+ (?<age>\S+) \s+ (?<joining>\S+) 
    | ^SAL: \s* (?<salary>\S+) 
    | ADDRESS: \s* (?<address>.*) 
"x; 

sub get_details{ 
    my %detail; 

    for my $input (@_) { 
    $input =~ $match; 
    @detail{keys %+} = values %+; 
    } 

    return \%detail; 
} 

use Data::Dumper; 
my @inputs = ('INFO: Vikram 32 2012', 'SAL: 12000$','ADDRESS: 54, junk, JUNK'); 
say Dumper get_details @inputs 
Cuestiones relacionadas