2010-12-11 34 views
2

je travaille avec un grand ensemble de données qui se résume essentiellement à quelque chose comme ceci:Élégamment Parsing données rigides en Perl

my $input = q(
<foo>111</foo> 
<foo>222</foo> 
<foo>333</foo> 
<foo></foo> 
<foo>555</foo> 
); # new-lines are either CR+LF, LF, or CR 

Sur la base de l'exemple ci-dessus, supposons que les contraintes suivantes sont en vigueur:

  • Il y aura toujours 5 lignes de données.
  • Les données de chaque ligne sont incluses dans une seule étiquette, par exemple <foo>...</foo>.
  • Les données ne contiendront pas de balises imbriquées.
  • Toutes les lignes utilisent la même étiquette (par exemple foo) pour entourer leurs données.

En fin de compte, en ce qui précède que la source de données, je voudrais finir avec quelque chose qui ressemble à ceci:

my %values = (
    one => '111', 
    two => '222', 
    three => '333', 
    four => '', 
    five => '555' 
); 

C'est ma tentative:

my @vals = $input =~ m!<foo>(.*?)</foo>!ig; 

if (scalar @vals != 5) { 
    # panic 
} 

my %values = (
    one => shift @vals, 
    two => shift @vals, 
    three => shift @vals, 
    four => shift @vals, 
    five => shift @vals 
); 

Cela fonctionne comme je veux, mais il a l'air moche et n'est pas très flexible. Malheureusement, c'est le meilleur que je peux faire pour l'instant depuis que je suis nouveau à Perl. Donc, compte tenu des contraintes ci-dessus, quelle est la manière la plus élégante de le faire?

Répondre

3

D'abord, prenez un autre regard sur:

my %values = (
    one => '111', 
    two => '222', 
    three => '333', 
    four => '', 
    five => '555' 
); 

Cette structure de données associe un entier avec un morceau de données. Mais il existe déjà une structure de données intégrée qui sert le même but: les tableaux.

Donc, utilisez des tableaux. Au lieu d'écrire $values{ one }, vous écrirez $values[ 0 ], et le mappage entre les entiers et les valeurs de données serait transparent.

Si les touches sont autre chose que des nombres entiers, vous pouvez le faire:

use strict; use warnings; 

my @keys = qw(a b c d e); 

my $input = q(
<foo>111</foo> 
<foo>222</foo> 
<foo>333</foo> 
<foo></foo> 
<foo>555</foo> 
); # new-lines are either CR+LF, LF, or CR 

my %values; 

# hash slice 
@values{ @keys } = $input =~ m{ <foo> (.*?) </foo>}gix; 

use YAML; 
print Dump \%values; 

Sortie:

--- 
a: 111 
b: 222 
c: 333 
d: '' 
e: 555
+0

Le dernier exemple est vraiment bien. Merci! – jnaturelle

+0

Le dernier exemple * est * assez soigné, mais suppose un nombre fixe de clés.Si vous voulez que ce soit plus flexible, et que vous génétiez les clés au fur et à mesure, «map» à une liste de taille égale (et en assignant cette liste au hash). 'map' et' grep' sont des amis. :) – fennec

2

Oh, quelque chose comme ça donner ou prendre?

use Number::Spell; 
$input =~ s|<(?:/)?foo>||g; 
my @lines = grep { $_ } split "\n", $input; # grep for blank lines 
my $i = 0; 
my %hash = map { spell_number($i++) => $_ } @lines; 

Hmm, je peux faire mieux.

use Number::Spell; 
my $i = 0; 
my %hash = map { s|<(?:/)?foo>||g; $_ ? spell_number($i++) => $_ :() } 
      split "\n", $input; 

ed. whoops, avait un @lines au lieu de $ input dans un second extrait. Soyez prudent. J'ai seulement tapé ce code; Je n'ai pas écrit de test unitaire.

4

La fusion de deux tableaux dans un hachage:

my @keys = qw/one two three/; 
my @values = qw/alpha beta gamma/; 

my %hash; 
@hash{@keys} = @values;