2010-12-11 3 views
2

Ich bin mit einem großen Datenmenge arbeiten, die im Grunde wie dies auf etwas kocht:Elegant Parsing Starre Daten in 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 

Basierend auf dem obigen Beispiel, nehmen wir an, dass die folgenden Einschränkungen in Kraft sind:

  • Es wird immer 5 Datenzeilen geben.
  • Daten in jeder Zeile sind in einem einzelnen Tag eingeschlossen, z. B. <foo>...</foo>.
  • Daten enthalten keine verschachtelten Tags.
  • Alle Zeilen verwenden das gleiche Tag (z. B. foo), um ihre Daten zu umschließen.

Letztlich die oben als Datenquelle nehmen, würde ich mit etwas, um am Ende wie ähnlich diese:

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

Dies ist mein Versuch:

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 
); 

Das funktioniert, wie ich will, aber es sieht hässlich aus und ist nicht sehr flexibel. Leider ist dies das Beste, was ich tun kann, seit ich neu bei Perl bin.

Also, angesichts der oben genannten Einschränkungen, was ist eine elegantere Möglichkeit, dies zu tun?

Antwort

3

Zuerst nehmen einen weiteren Blick auf:

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

Diese Datenstruktur zuordnet eine ganze Zahl mit einem Stück von Daten. Aber es gibt bereits eine eingebaute Datenstruktur, die dem gleichen Zweck dient: Arrays.

Verwenden Sie also Arrays. Anstatt $values{ one } zu schreiben, würden Sie $values[ 0 ] schreiben, und die Zuordnung zwischen Ganzzahlen und Datenwerten wäre transparent.

Wenn die Tasten sind etwas anderes als ganze Zahlen sind, können Sie tun:

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; 

Ausgang:

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

Das letzte Beispiel ist wirklich ordentlich. Vielen Dank! – jnaturelle

+0

Das letzte Beispiel * ist * irgendwie ordentlich, nimmt aber eine feste Anzahl von Schlüsseln an.Wenn Sie möchten, dass es flexibler ist und die Schlüssel generiert, während Sie weitermachen, "ordnen" Sie sich einer Liste mit einer geraden Größe zu (und weisen Sie diese Liste dem Hash zu). 'map' und' grep' sind Freunde. :) – fennec

2

Oh, so etwas geben oder nehmen?

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, ich kann das besser machen.

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

ed. Whoops, hatte ein @lines anstelle von $ inna zweites Snippet. Vorsicht walten lassen; Ich habe diesen Code nur eingegeben; Ich habe keinen Unit Test geschrieben.

4

Merging zwei Arrays in eine hash:

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

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