2016-06-22 14 views
2

Ich bin mir nicht sicher, wie ich meinen Hash richtig initialisiere - Ich versuche, ein Schlüssel/Wert-Paar für Werte in gekoppelten Zeilen in meiner Eingabedatei zu erstellen.Perl - Initialisierung von Hash

Zum Beispiel sieht meine Eingabe wie folgt aus:

@cluster t.18 
46421 ../../../output###.txt/ 
@cluster t.34 
41554 ../../../output###.txt/ 

Ich Extrahieren der T-Nummer von Zeile 1 (@cluster Linie) und zur Ausgabe passend zu ### in der zweiten Zeile txt (. Zeile beginnend mit 46421). Ich kann jedoch nicht scheinen, diese Werte mit dem Skript, das ich geschrieben habe, in meinen Hash zu bekommen.

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

my $key; 
my $value; 
my %hash; 

my $filename = 'input.txt'; 
open my $fh, '<', $filename or die "Can't open $filename: $!"; 

while (my $line = <$fh>) { 
     chomp $line; 
     if ($line =~ m/^\@cluster/) { 
      my @fields = split /(\d+)/, $line; 
      my $key = $fields[1];   
     } 
     elsif ($line =~ m/^(\d+)/) { 
      my @output = split /\//, $line; 
      my $value = $output[5];  
}   
     $hash{$key} = $value; 
} 
+1

Lassen Sie die 'my' von' my $ key' und 'my $ value' in die' if/elsif' Blöcke fallen, da dies _new_ Variablen erzeugt und die globalen Variablen versteckt. Aber was ist das erwartete Ergebnis? '{1 => 'Ausgabe ###. Txt', 2 => 'Ausgabe ###. Txt'}'? – PerlDuck

+0

@PerlDog Danke für die Rückmeldung. Ja, der Hash, den ich möchte, ist '{1 => 'Ausgabe ###. Txt', 2 => 'Ausgabe ###. Txt'}' - Ich würde einfach einen Zähler hinzufügen, aber die Zahlen sind nicht drin Bestellung in meiner Eingabedatei. – EA00

+0

Ok, und wie bezieht sich die "1" in Zeile 1 auf die "1111" in Zeile 2? Warum passen sie zusammen? Weil die 2. Zeile mit 4 mal der Nummer von Zeile 1 beginnt oder weil es 'number + 1110' ist? Oder einfach, weil es die nächste Zeile ist? Bitte zeigen Sie einige Eingaben an, die nicht mehrdeutig sind. – PerlDuck

Antwort

6

Es ist eine gute Idee, aber Ihr $key dass mit my im ifBlock erstellt wird, ist eine lokale Variable zu diesem Block scoped, die globale $key maskieren. Innerhalb des Blocks if hat das Symbol $key nichts mit dem zu tun, das Sie im Vorfeld gut deklariert haben. Siehe my in perlsub.

Dieser lokale $key geht aus dem Geltungsbereich, sobald if ist getan und existiert nicht außerhalb des if Block. Die globale $key ist wieder verfügbar nach der if, sichtbar an anderer Stelle in der Schleife, aber ist nicht definiert, da es nie zugewiesen wurde. Das gleiche gilt für $value im elsifBlock.

Lassen Sie einfach die my Deklaration innerhalb der Schleife fallen, also diesen globalen Variablen zuweisen (wie vorgesehen?). Also, $key = ... und $value = ..., und der Hash wird korrekt zugewiesen.


Hinweis - hier geht es darum, wie Sie diese Hash-Zuweisung richtig erhalten. Ich weiß nicht, wie Ihre tatsächlichen Daten aussehen und ob die Zeile korrekt analysiert wird. Hier ist ein Spielzeug input.txt

 
@cluster t.1 
1111 ../../../output1.1.txt/ 
@cluster t.2 
2222 ../../../output2.2.txt/ 

Ich nehme das 4. Feld anstatt den 6., $value = $output[3];, und fügen Sie

print "$_ => $hash{$_}\n" for keys %hash; 

nach der Schleife. Diese Drucke

Ich bin nicht sicher, ob das ist, was Sie wollen, aber der Hash ist gut gebaut.


Ein Kommentar über Auswahl von Werkzeugen in

Parsen analysieren Sie die Linien für Zahlen, durch die Eigenschaft split mit den Separatoren als auch zurückkehren, wenn sie erfasst werden. Das ist ordentlich, aber in gewisser Hinsicht kehrt es seinen Hauptzweck um, nämlich andere Komponenten aus der Zeichenfolge zu extrahieren, wie durch das Muster begrenzt. So kann es den Zweck des Codes ein wenig verschachtelt machen, und Sie müssen auch sehr genau indexieren, um zu erhalten, was Sie brauchen.

Anstatt split zu verwenden, um das Trennzeichen selbst zu extrahieren, das durch eine Regex gegeben wird, warum nicht es durch eine Regex extrahieren? Das macht die Absicht auch klar.Zum Beispiel mit Eingang

 
@cluster t.10 has 4319 elements, 0 subclusters 
37652 ../../../../clust/output43888.txt 1.397428 

kann die Analyse gehen als

if ($line =~ m/^\@cluster/) { 
    ($key) = $line =~ /t\.(\d+)/; 
} 
elsif ($line =~ m/^(\d+)/) { 
    ($value) = $line =~ m|.*/(\w+\.txt)|; 
}  
$hash{$key} = $value if defined $key and defined $value; 

wo t\. und \.txt werden hinzugefügt, um genauer die Ziele angeben. Wenn die Zielzeichenfolgen nicht sicher sind, dass sie genau diese Form haben, erfassen Sie einfach \d+, und im zweiten Fall alle nicht Leerzeichen nach der letzten /, sagen wir m|^\d+.*/(\S+)|. Wir verwenden die Gierigkeit von .*, die alles möglich ist bis zu der Sache, die danach kommt (a /), also den ganzen Weg bis zum allerletzten /.

Dann können Sie auch

if ($line =~ m/^\@cluster\s+t\.(\d+)/) { 
    $key = $1; 
} 
elsif ($line =~ m|^\d+.*/(\w+\.txt)|) { 
    $value = $1; 
} 

Hinweis zum Beispiel für jede Zeile, auf einen einzigen regulären Ausdruck reduzieren, dass ich eine Bedingung für die Hash-Zuordnung hinzugefügt habe. Der ursprüngliche Code ordnet der ersten Iteration tatsächlich einen undef zu, da zu diesem Zeitpunkt noch kein $value gesehen wurde. Dies wird bei der nächsten Iteration überschrieben und wir sehen es nicht, wenn wir den Hash anschließend nur ausdrucken. Die Bedingung schützt Sie auch vor fehlgeschlagenen Übereinstimmungen, vor falsch formatierten Zeilen oder ähnlichem. Natürlich können weitaus bessere Kontrollen durchgeführt werden.

+0

@PerlDog die Spaltung funktioniert für mich bei der Extrahierung der Anzahl und Ausgabe für jede entsprechende Zeile - Ich kann nur nicht den Hash zu initialisieren. Wie auch immer, dein Vorschlag, die meine Werke fallen zu lassen! Vielen Dank! – EA00

+0

@zdim Ich habe versucht, herauszufinden, wie man den Hash drucken - also danke. Auch Ihre Erklärung ist sehr sinnvoll. – EA00

+0

@PerlDog Fairer Punkt, danke, ich habe die beleidigende Aussage geändert, um zu sagen, dass der Hash in Ordnung ist, nicht alles andere. (Beachten Sie jedoch, was ich hinzugefügt habe - Split nimmt tatsächlich die Nummer auf.) Ich werde auch das Parsing reparieren. Vielen Dank. – zdim