3 Stimmen

Wie kann ich diese Zeichenketten in Perl in einen Hash umwandeln?

Ich möchte eine einzelne Zeichenkette mit mehreren Begrenzungszeichen in eine Schlüssel=>Wert-Hash-Struktur konvertieren. Gibt es eine einfache Möglichkeit, dies zu erreichen? Meine derzeitige Implementierung ist:

sub readConfigFile() {
    my %CONFIG;
    my $index = 0;
    open(CON_FILE, "config");
    my @lines = <CON_FILE>;
    close(CON_FILE);

    my @array = split(/>/, $lines[0]);
    my $total = @array;

    while($index < $total) {
        my @arr = split(/=/, $array[$index]); 
        chomp($arr[1]);
        $CONFIG{$arr[0]} = $arr[1];       
        $index = $index + 1; 
    }

    while ( ($k,$v) = each %CONFIG ) {
        print "$k => $v\n";
    }

    return;
}

wobei 'config' enthält:

pub=3>rec=0>size=3>adv=1234 123 4.5 6.00
pub=1>rec=1>size=2>adv=111 22 3456 .76

Die letzten Ziffern müssen ebenfalls entfernt und in einem separaten Schlüssel=>Wertpaar gespeichert werden, dessen Name "ip" sein kann. (Es ist mir nicht gelungen, dies zu erreichen, ohne den Code zu lang und kompliziert zu machen).

4voto

brian d foy Punkte 124323

Wie soll Ihre Konfigurationsdatenstruktur aussehen? Bislang zeichnen die Lösungen nur die letzte Zeile auf, weil sie bei jedem Hinzufügen eines Datensatzes auf denselben Hash-Schlüsseln herumtrampeln.

Hier ist etwas, das Sie vielleicht weiterbringt, aber Sie müssen noch herausfinden, wie die Datenstruktur aussehen soll.

  • Ich übergebe das Dateihandle als Argument, damit mein Unterprogramm nicht an eine bestimmte Art und Weise gebunden ist, die Daten zu erhalten. Die Daten können aus einer Datei, einer Zeichenkette, einem Socket oder sogar aus dem folgenden Material stammen DATEN in diesem Fall.

  • Anstatt die Dinge zu korrigieren, nachdem ich die Zeichenkette analysiert habe, korrigiere ich die Zeichenkette so, dass sie das Element "ip" enthält, bevor ich sie analysiere. Danach ist das "ip"-Element kein Sonderfall mehr und es ist nur noch eine Frage des doppelten Splits. Dies ist eine sehr wichtige Technik, die eine Menge Arbeit und Code spart.

  • Ich erstelle eine Hash-Referenz innerhalb des Unterprogramms und gebe diese Hash-Referenz zurück, wenn ich fertig bin. Ich benötige keine globale Variable :)

    use warnings; use strict;

    use Data::Dumper;

    readConfigFile( \*DATA );

    sub readConfigFile { my( $fh ) = shift;

    my $hash = {};
    
    while( <$fh> )
        {
        chomp;
    
        s/\\s+(\\d\*\\.\\d+)$/>ip=$1/;
    
        $hash->{ $. } = { map { split /=/ } split />/ };
        }
    
    return $hash;
    }

    my $hash = readConfigFile( \*DATA );

    print Dumper( $hash );

    __DATA__ pub=3>rec=0>size=3>adv=1234 123 4.5 6.00 pub=1>rec=1>size=2>adv=111 22 3456 .76

So erhalten Sie eine Datenstruktur, bei der jede Zeile ein eigener Datensatz ist. Ich wähle die Zeilennummer des Datensatzes ( $. ) als Schlüssel der obersten Ebene, aber Sie können alles verwenden, was Sie wollen.

$VAR1 = {
          '1' => {
                   'ip' => '6.00',
                   'rec' => '0',
                   'adv' => '1234 123 4.5',
                   'pub' => '3',
                   'size' => '3'
                 },
          '2' => {
                   'ip' => '.76',
                   'rec' => '1',
                   'adv' => '111 22 3456',
                   'pub' => '1',
                   'size' => '2'
                 }
        };

Wenn das nicht die von Ihnen gewünschte Struktur ist, zeigen Sie uns, was Sie sich wünschen, und wir können unsere Antworten anpassen.

2voto

Chris Charley Punkte 6091

Ich gehe davon aus, dass Sie mehr als 1 Zeile lesen und analysieren wollen. Deshalb habe ich mich entschieden, die Werte in einem AoH zu speichern.

#!/usr/bin/perl
use strict;
use warnings;

my @config;

while (<DATA>) {
    chomp;
    push @config, { split /\[=>\]/ };
}

for my $href (@config) {
    while (my ($k, $v) = each %$href) {
        print "$k => $v\\n";
    }
}

\_\_DATA\_\_
pub=3>rec=0>size=3>adv=1234 123 4.5 6.00
pub=1>rec=1>size=2>adv=111 22 3456 .76

Das Ergebnis ist der untenstehende Ausdruck. (Die obige while-Schleife liest aus DATA.)

rec => 0
adv => 1234 123 4.5 6.00
pub => 3
size => 3
rec => 1
adv => 111 22 3456 .76
pub => 1
size => 2

Chris

1voto

Jonathan Leffler Punkte 694013

Das Format der Konfigurationsdatei ist, sagen wir mal, suboptimal. Das heißt, es gibt einfachere Formate zum Parsen und Verstehen. [ Hinzugefügt : aber das Format ist bereits von einem anderen Programm definiert. Perl ist flexibel genug, um damit umzugehen].

Ihr Code schlürft die Datei, wenn kein wirklicher Bedarf besteht.

Ihr Code beachtet nur die letzte Datenzeile in der Datei (wie Chris Charley feststellte, als ich diesen Text schrieb).

Sie haben auch keine Kommentarzeilen oder Leerzeilen zugelassen - beides ist eine gute Idee in jeder Konfigurationsdatei und sie sind leicht zu unterstützen. [ Hinzugefügt Auch hier gilt: Bei dem vordefinierten Format ist dies kaum relevant, aber wenn Sie Ihre eigenen Dateien entwerfen, sollten Sie daran denken].

Hier ist eine Anpassung Ihrer Funktion in etwas idiomatischerem Perl.

#!/bin/perl -w
use strict;
use constant debug => 0;

sub readConfigFile()
{
    my %CONFIG;
    open(CON_FILE, "config") or die "failed to open file ($!)\n";

    while (my $line = <CON_FILE>)
    {
        chomp $line;
        $line =~ s/#.*//;           # Remove comments
        next if $line =~ /^\s*$/;   # Ignore blank lines

        foreach my $field (split(/>/, $line))
        {
            my @arr = split(/=/, $field);
            $CONFIG{$arr[0]} = $arr[1];
            print ":: $arr[0] => $arr[1]\n" if debug;
        }
    }
    close(CON_FILE);

    while (my($k,$v) = each %CONFIG)
    {
        print "$k => $v\n";
    }
    return %CONFIG;
}

readConfigFile;    # Ignores returned hash

Jetzt müssen Sie genauer erklären, wie das letzte Feld strukturiert ist und warum Sie ein "ip"-Feld ohne die Notation Schlüssel=Wert haben. Konsistenz macht das Leben für alle einfacher. Sie müssen auch darüber nachdenken, wie mit mehreren Zeilen umgegangen werden soll. Und ich würde eine orthodoxere Notation in Erwägung ziehen, wie zum Beispiel:

pub=3;rec=0;size=3;adv=(1234,123,4.5);ip=6.00

Doppelpunkt oder Semikolon als Begrenzungszeichen sind durchaus üblich; Klammern um durch Komma getrennte Elemente in einer Liste sind keine abwegige Konvention. Konsistenz ist das A und O. Emerson sagte: "Eine törichte Konsistenz ist der Kobold kleiner Geister, verehrt von kleinen Staatsmännern und Philosophen und Göttlichen", aber Konsistenz in der Informatik ist ein großer Vorteil für alle.

1voto

Altreus Punkte 5099

Im Folgenden wird davon ausgegangen, dass das Begrenzungszeichen garantiert ein > ist und in den Daten nicht vorkommt.

Ich habe einfach jede Zeile anhand von '>' aufgeteilt. Der letzte Wert wird ein Schlüssel=Wert-Paar enthalten, dann ein Leerzeichen, dann die IP, so teilen Sie diese auf / / genau einmal (Grenze 2) und Sie erhalten die k=v und die IP. Speichern Sie die IP im Hash und behalten Sie das k=v-Paar im Array, gehen Sie dann durch das Array und teilen Sie k=v an '='.

Füllen Sie den Hashref aus und schieben Sie ihn in Ihr Array mit höherer Skalierung. Dieses enthält dann Ihre Hash-Refs, wenn Sie fertig sind.

(nachdem die Konfiguration in ein Array geladen wurde)

my @hashes;

for my $line (@config) {
    my $hash; # config line will end up here

    my @pairs = split />/, $line;

    # Do the ip first. Split the last element of @pairs and put the second half into the
    # hash, overwriting the element with the first half at the same time.
    # This means we don't have to do anything special with the for loop below.
    ($pairs[-1], $hash->{ip}) = (split / /, $pairs[-1], 2);

    for (@pairs) {
        my ($k, $v) = split /=/;
        $hash->{$k} = $v;
    }

    push @hashes, $hash;
}

0voto

Martin Redmond Punkte 12146

Hier ist eine Möglichkeit.

foreach ( @lines ) {
  chomp;
  my %CONFIG;
  # Extract the last digit first and replace it with an end of
  # pair delimiter.
  s/\\s\*(\[\\d\\.\]+)\\s\*$/>/;
  $CONFIG{ip} = $1;
  while ( /(\[^=\]\*)=(\[^>\]\*)>/g ) {
    $CONFIG{$1} = $2;
  }
  print Dumper ( \\%CONFIG );
}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X