2011-01-06 12 views
0

ich die Textdatei habe:Parsing YAML-like Textdatei in Hash-Struktur

country = { 
    tag = ENG 
    ai = { 
     flags = { } 
     combat = { ROY WLS PUR SCO EIR FRA DEL USA QUE BGL MAH MOG VIJ MYS DLH GUJ ORI JAI ASS MLC MYA ARK PEG TAU HYD } 
     continent = { "Oceania" } 
     area = { "America" "Maine" "Georgia" "Newfoundland" "Cuba" "Bengal" "Carnatic" "Ceylon" "Tanganyika" "The Mascarenes" "The Cape" "Gold" "St Helena" "Guiana" "Falklands" "Bermuda" "Oregon" } 
     region = { "North America" "Carribean" "India" } 
     war = 50 
     ferocity = no 
    } 
    date = { year = 0 month = january day = 0 } 
} 

Was ich versuche zu tun, diesen Text in Perl-Hash-Struktur zu analysieren, so dass die Ausgabe nach Daten Dump sieht wie folgt aus:

$VAR1 = { 
      'country' => { 
         'ai' => { 
            'area' => [ 
               'America', 
               'Maine', 
               'Georgia', 
               'Newfoundland', 
               'Cuba', 
               'Bengal', 
               'Carnatic', 
               'Ceylon', 
               'Tanganyika', 
               'The Mascarenes', 
               'The Cape', 
               'Gold', 
               'St Helena', 
               'Guiana', 
               'Falklands', 
               'Bermuda', 
               'Oregon' 
              ], 
            'combat' => [ 
               'ROY', 
               'WLS', 
               'PUR', 
               'SCO', 
               'EIR', 
               'FRA', 
               'DEL', 
               'USA', 
               'QUE', 
               'BGL', 
               'MAH', 
               'MOG', 
               'VIJ', 
               'MYS', 
               'DLH', 
               'GUJ', 
               'ORI', 
               'JAI', 
               'ASS', 
               'MLC', 
               'MYA', 
               'ARK', 
               'PEG', 
               'TAU', 
               'HYD' 
               ], 
            'continent' => [ 
                'Oceania' 
                ], 
            'ferocity' => 'no', 
            'flags' => [], 
            'region' => [ 
               'North America', 
               'Carribean', 
               'India' 
               ], 
            'war' => 50 
           }, 
         'date' => { 
            'day' => 0, 
            'month' => 'january', 
            'year' => 0 
            }, 
         'tag' => 'ENG' 
         } 
     }; 

Fest programmierte Version könnte wie folgt aussehen:

#!/usr/bin/perl 
use Data::Dumper; 
use warnings; 
use strict; 

my $ret; 

$ret->{'country'}->{tag} = 'ENG'; 
$ret->{'country'}->{ai}->{flags} = []; 
my @qw = qw(ROY WLS PUR SCO EIR FRA DEL USA QUE BGL MAH MOG VIJ MYS DLH GUJ ORI JAI ASS MLC MYA ARK PEG TAU HYD); 
$ret->{'country'}->{ai}->{combat} = \@qw; 
$ret->{'country'}->{ai}->{continent} = ["Oceania"]; 
$ret->{'country'}->{ai}->{area} = ["America", "Maine", "Georgia", "Newfoundland", "Cuba", "Bengal", "Carnatic", "Ceylon", "Tanganyika", "The Mascarenes", "The Cape", "Gold", "St Helena", "Guiana", "Falklands", "Bermuda", "Oregon"]; 
$ret->{'country'}->{ai}->{region} = ["North America", "Carribean", "India"]; 
$ret->{'country'}->{ai}->{war} = 50; 
$ret->{'country'}->{ai}->{ferocity} = 'no'; 
$ret->{'country'}->{date}->{year} = 0; 
$ret->{'country'}->{date}->{month} = 'january'; 
$ret->{'country'}->{date}->{day} = 0; 

sub hash_sort { 
    my ($hash) = @_; 
    return [ (sort keys %$hash) ]; 
} 

$Data::Dumper::Sortkeys = \hash_sort; 

print Dumper($ret); 

ich ich habe ein großes Problem de zugeben mit verschachtelten geschweiften Klammern umgehen. Ich habe versucht, es zu lösen, indem ich gierige und ungegenständliche Anpassung verwende, aber es scheint, dass es nicht den Trick gemacht hat. Ich habe auch über erweiterte Muster gelesen (wie (? PARNO)), aber ich habe absolut keine Ahnung, wie man sie in meinem speziellen Problem verwendet. Die Reihenfolge der Daten ist irrelevant, da ich die Unterroutine hash_sort habe. Ich werde jede Hilfe applaudieren.

+2

Was erstellt die Textdatei. Meine Lösung wäre, eine Möglichkeit zu finden, die Textdatei so zu erstellen, dass sie wirklich eine YAML-Datei ist. Es ist verrückt, es anders zu machen! Es ist einfacher, es in einem Standardformat zu erstellen, und es ist einfacher, es zu lesen! –

+0

Paradox Savefiles, nicht wahr? – Oesor

+0

Europa Universalis um genau zu sein;). Aber es geht nicht um das Hacken von Spielen, sondern darum, ein Collage-Projekt zu machen. – marooou

Antwort

3

ich es brach auf einige einfachen Annahmen nach unten:

  1. Ein Eintrag würde von einem Gleichheits gefolgt von einer Kennung besteht
  2. unterzeichnen
  3. Ein Eintrag einer der drei Grundtypen sein würde: eine Ebene oder einen Satz oder ein Einzelwert
  4. Ein Satz hat 3 Formen: 1) zitiert, durch Leerzeichen getrennte Liste; 2) Schlüssel-Wert-Paare, 3) QW-like unquoted Liste
  5. Ein Satz von Schlüssel-Wert-Paare muss einen indentifier für einen Schlüssel, und entweder nonspaces oder notierten Wert für einen Wert enthalten

Siehe eingestreute Kommentare.

use strict; 
use warnings; 

my $simple_value_RE 
    = qr/^ \s* (\p{Alpha}\w*) \s* = \s* ([^\s{}]+ | "[^"]*") \s* $/x 
    ; 
my $set_or_level_RE 
    = qr/^ \s* (\w+) \s* = \s* [{] (?: ([^}]+) [}])? \s* $/x 
    ; 
my $quoted_set_RE 
    = qr/^ \s* (?: "[^"]+" \s+)* "[^"]+" \s* $/x 
    ; 
my $associative_RE 
    = qr/^ \s* 
     (?: \p{Alpha}\w* \s* = \s* (?: "[^"]+" | \S+) \s+)* 
     \p{Alpha}\w* \s* = \s* (?: "[^"]+" | \S+) 
     \s* $ 
    /x 
    ; 
my $pair_RE = qr/ \b (\p{Alpha}\w*) \s* = \s* ("[^"]+" | \S+)/x; 

sub get_level { 
    my $handle = shift; 
    my %level; 
    while (<$handle>) { 
     # if the first character on the line is a close, then we're done 
     # at this level 
     last if m/^\s*[}]/; 
     my ($key, $value); 

     # get simple values 
     if (($key, $value) = m/$simple_value_RE/) { 
      # done. 
     } 
     elsif (($key, my $complete_set) = m/$set_or_level_RE/) { 
      if ($complete_set) { 
       if ($complete_set =~ m/$quoted_set_RE/) { 
        # Pull all quoted values with global flag 
        $value = [ $complete_set =~ m/"([^"]+)"/g ]; 
       } 
       elsif ($complete_set =~ m/$associative_RE/) { 
        # going to create a hashref. First, with a global flag 
        # repeatedly pull all qualified pairs 
        # then split them to key and value by spliting them at 
        # the first '=' 
        $value 
         = { map { split /\s*=\s*/, $_, 2 } 
           ($complete_set =~ m/$pair_RE/g) 
         }; 
       } 
       else { 
        # qw-like 
        $value = [ split(' ', $complete_set) ]; 
       } 
      } 
      else { 
       $value = get_level($handle); 
      } 
     } 
     $level{ $key } = $value; 
    } 
    return wantarray ? %level : \%level; 
} 

my %base = get_level(\*DATA); 
2

Nun, wie David vorgeschlagen hat, wäre der einfachste Weg zu bekommen, was auch immer die Datei erzeugte, um ein Standardformat zu verwenden. JSON, YAML oder XML wäre viel einfacher zu parsen.

Aber wenn Sie wirklich dieses Format analysieren müssen, würde ich eine Grammatik dafür mit Regexp::Grammars schreiben (wenn Sie Perl 5.10 benötigen) oder Parse::RecDescent (wenn Sie nicht können). Dies wird ein wenig schwierig, vor allem, weil Sie scheinen Klammern für beide Hashes & Arrays zu verwenden, aber es sollte machbar sein.

2

Der Inhalt sieht ziemlich regelmäßig aus. Warum nicht einige Substitutionen am Inhalt durchführen und ihn in Hash-Syntax umwandeln, dann evaluiere ihn. Das wäre ein schneller und schmutziger Weg, um es zu konvertieren.

Sie können auch einen Parser schreiben, vorausgesetzt, Sie kennen die Grammatik.

+0

Ich kann das nicht tun. Es ist wahr, dass das schnell gehen würde, aber leider muss es auch sauber sein. Danke trotzdem – marooou