Andreas Hoppe: Hash aus Datei auslesen

Hi Leute,

ich habe ein kleines Problem mit dem Auslesen von Daten aus einer Datei.

In meine Datei test.txt werden Einträge gespeichert. Jeder Eintrag wird mit Hilfe eines Hash %datensatz erstellt.

Das Ganze sieht momentan so bei mir aus:

my %datensatz = (Name => "Mustername", Email => "mustermail@nix.de");
open (DATEI, "<test.txt");
print DATEI %datensatz;
close (DATEI);

So weit so gut, funktioniert auch.

Aber wie kann ich die Datensätze wieder auslesen? Ich möchte die einzelnen Einträge in einem "Array of Hashes" haben.

Das Array deklariere ich mittels:
my @datensaetze = ();

Die Perl-Doku hilft mir da nicht allzuviel weiter. :-(

Grüße

Andreas

  1. Moin!

    Aber wie kann ich die Datensätze wieder auslesen? Ich möchte die einzelnen Einträge in einem "Array of Hashes" haben.

    Wenn Du das vorher mit print %hash ausgibst, kannst Du das vergessen. Du wirst nicht drum herum kommen, Dir eine geeignete Datenstruktur auszudenken und entsprechende Input- und Output-Routinen zu schreiben. Falls das ganze aber so eine Art Datenbank-Tabelle sein soll, wo jeder Hash eine Zeile enthaelt, koennte Dir das Modul DBI in Verbindung mit DBD::CSV weiterhelfen.

    So long

    --
    Real Life: big room with ceiling that is sometimes blue and sometimes black with little lights.
    1. Hi!

      Wenn Du das vorher mit print %hash ausgibst, kannst Du das vergessen. Du wirst nicht drum herum kommen, Dir eine geeignete Datenstruktur auszudenken und entsprechende Input- und Output-Routinen zu schreiben.

      Das hab ich mittlerweile auch gemerkt. Einzig das Modul DB_File würde wohl diese Funktionalität bieten.

      Grüße und Dank

      Andreas

  2. Hi Andreas

    vielleicht hilft dir ja folgendes weiter:
    aus
    "Programming Perl"
    by Larry Wall, Tom Christiansen & Randal L. Schwartz; ISBN 1-56592-149-6, 670 pages.
    Second Edition, September 1996.

    <snippet>
    4.7.3 Arrays of Hashes

    An array of hashes is called for when you have a bunch of records that you'd like to access sequentially, but each record itself contains key/value pairs. These arrays tend to be used less frequently than the other homogeneous data structures.
    4.7.3.1 Composition of an array of hashes

    @LoH = (
        {
           lead     => "fred",
           friend   => "barney",
        },
        {
           lead    => "george",
           wife    => "jane",
           son     => "elroy",
        },
        {
           lead    => "homer",
           wife    => "marge",
           son     => "bart",
        },
      );

    4.7.3.2 Generation of an array of hashes

    reading from file

    format: lead=fred friend=barney

    while ( <> ) {
        $rec = {};
        for $field ( split ) {
            ($key, $value) = split /=/, $field;
            $rec->{$key} = $value;
        }
        push @LoH, $rec;
    }

    reading from file

    format: lead=fred friend=barney

    no temp

    while ( <> ) {
        push @LoH, { split /[\s=]+/ };
    }

    calling a function that returns a key,value array, like

    "lead","fred","daughter","pebbles"

    while ( %fields = getnextpairset() ) {
        push @LoH, { %fields };
    }

    likewise, but using no temp vars

    while (<>) {
        push @LoH, { parsepairs($_) };
    }

    add key/value to an element

    $LoH[0]{pet} = "dino";
    $LoH[2]{pet} = "santa's little helper";

    4.7.3.3 Access and printing of an array of hashes

    one element

    $LoH[0]{lead} = "fred";

    another element

    $LoH[1]{lead} =~ s/(\w)/\u$1/;

    print the whole thing with refs

    for $href ( @LoH ) {
        print "{ ";
        for $role ( keys %$href ) {
             print "$role=$href->{$role} ";
        }
        print "}\n";
    }

    print the whole thing with indices

    for $i ( 0 .. $#LoH ) {
        print "$i is { ";
        for $role ( keys %{ $LoH[$i] } ) {
             print "$role=$LoH[$i]{$role} ";
        }
        print "}\n";
    }

    print the whole thing one at a time

    for $i ( 0 .. $#LoH ) {
        for $role ( keys %{ $LoH[$i] } ) {
             print "element $i $role is $LoH[$i]{$role}\n";
        }
    }

    </snippet>

    Bye
    Timothy

    --
    Zwei Dinge im Leben kannst du nicht zurück holen. Den Pfeil, den du verschossen. Und das Wort, das du gesprochen.
    (alte indianische Weisheit)
    1. Hi Timothy,

      reading from file

      format: lead=fred friend=barney

      while ( <> ) {
          $rec = {};
          for $field ( split ) {
              ($key, $value) = split /=/, $field;
              $rec->{$key} = $value;
          }
          push @LoH, $rec;
      }

      wenn ich das richtig sehe, dann müssen in beiden "reading from file"-Beispielen die Daten vorher in der Art key=value gespeichert werden.

      Ich behalt's aber mal im Auge, weil, so wie ich das sehe, sonst keine andere Möglichkeit besteht direkt an das Hash zu kommen.

      Grüße und Danke

      Andreas

  3. Hi,

    du willst 2 Dinge kennelernen.

    Einmal

    use Data::Dumper;

    gibt dir deine Variabeln aus.

    use Data::Dumper;

    my %HASH = { bla => 1, blubb => 2};

    open F, ...

    print F Dumper %HASH;

    close;

    und einlesen:

    open F ...

    my $VAR1 = <F>;
    eval ($VAR1);

    my %HASH = %{$VAR1};

    oder
    use DB_File;

    damit kannst du Hashes dierekt speichern.

    us DB_File;

    my %HASH;

    tie %HASH , 'DB_File', 'datei', O_RDWR|O_CREAT, 0777;

    %HASH{neu}  =  'neuer Eintrag';

    super Sache, vor allem in Verbindung mit Data::Dumper.

    Struppi.

    1. Halihallo Struppi

      du willst 2 Dinge kennelernen.
      use Data::Dumper;
      gibt dir deine Variabeln aus.

      Es gibt eine menschenlesbare Repräsentation der Datenstruktur aus, ja.

      use Data::Dumper;
      my %HASH = { bla => 1, blubb => 2};

      Variablen sind keine Konstanten und werden klein geschrieben. Zudem, was wohl auf einen
      Tippfehler zurückzuführen ist, schreibt man my %hash = () mit runden Klammern ;)

      open F, ...
      print F Dumper %HASH;
      close;

      und einlesen:

      open F ...
      my $VAR1 = <F>;
      eval ($VAR1);
      my %HASH = %{$VAR1};

      Äm, sorry, aber das ist zwar funktional aber völliger Schwachsinn.
      Wenn du schon komplexe Datenstrukturen speichern willst, dann verwende

      perldoc Storable;

      aber ein aperformantes generieren eines schönen Abbildes der Datenstruktur und ein
      aperformantes einlesen über kurriose eval's (was bei Attacken auch lauffähiger Perl-Code
      sein könnte) ist schlicht, naja etwas viel "Overhead" ;)

      oder
      use DB_File;

      schon besser ;)

      super Sache, vor allem in Verbindung mit Data::Dumper.

      Data::Dumper erstellt ein Dump, also Abbild einer Datenstruktur und ist somit _nicht_
      für die Speicherung derselben gedacht.

      Viele Grüsse

      Philipp

      PS: Nicht bös gemeint, aber Data::Dumper mit eval sollte man wirklich nicht verwenden,
      auch wenn's auf den ersten Blick "schön einfach und lustig" aussieht.

      1. Hi Philipp

        open F, ...
        print F Dumper %HASH;
        close;

        und einlesen:

        open F ...
        my $VAR1 = <F>;
        eval ($VAR1);
        my %HASH = %{$VAR1};

        Äm, sorry, aber das ist zwar funktional aber völliger Schwachsinn.
        Wenn du schon komplexe Datenstrukturen speichern willst, dann verwende

        perldoc Storable;

        aber ein aperformantes generieren eines schönen Abbildes der Datenstruktur und ein
        aperformantes einlesen über kurriose eval's (was bei Attacken auch lauffähiger Perl-Code
        sein könnte) ist schlicht, naja etwas viel "Overhead" ;)

        Mhh, ich verstehe die Einwände. Aber

        Bevor ich den Umstieg auf mySQL gewagt habe, habe ich meine "Datenbanken" immer so benutzt. Storable hat den Nachteil, das ich alle Daten einlesen muss, was wohl selbst bei einer kleinen DB nicht unbedeutsam ist.

        eval sollte aber keine Probleme machen, da der Inhalt ja nicht ausgeführt wird, sondern der output von Data::Dumper, der (oder verläßt mich da meine Vorstellungskraft) keinen Code enthält, sondern Strings, die die Datenstruktur darstellt.

        Struppi.

        P.S. ich teile so viel Kritik aus, da sollte man durchaus auch Kritik einstecken können ;-)

        1. Halihallo Struppi

          Äm, sorry, aber das ist zwar funktional aber völliger Schwachsinn.
          Wenn du schon komplexe Datenstrukturen speichern willst, dann verwende

          perldoc Storable;

          aber ein aperformantes generieren eines schönen Abbildes der Datenstruktur und ein
          aperformantes einlesen über kurriose eval's (was bei Attacken auch lauffähiger Perl-Code
          sein könnte) ist schlicht, naja etwas viel "Overhead" ;)
          Bevor ich den Umstieg auf mySQL gewagt habe, habe ich meine "Datenbanken" immer so benutzt. Storable hat den Nachteil, das ich alle Daten einlesen muss, was wohl selbst bei einer kleinen DB nicht unbedeutsam ist.

          Wieso? - Storable liest genauso die ganze Datei ein, wie du auch über deinen Lösungsweg
          machen musst. Zudem hat Storable ein C-Backend, was es doch ganz schön schnell macht,
          eval hingegen (wohl auch auf C basierend...) muss den eingelesenen String zuerst
          parsen, dann wird er in einen Perl-Parsed-Tree umgewandelt und dann interpretiert, wie
          das mit allen Perlprogrammen geschieht; das macht die Sache eben langsam.

          eval sollte aber keine Probleme machen, da der Inhalt ja nicht ausgeführt wird, sondern der output von Data::Dumper, der (oder verläßt mich da meine Vorstellungskraft) keinen Code enthält, sondern Strings, die die Datenstruktur darstellt.

          Auch Strings gehören zu Perl und werden interpretiert, genauso wie Befehle. Natürlich
          gibt es bei Data::Dumper nicht viel "auszuführen", dennoch wird der Code (und _das_ ist
          er dennoch) zuerst geparsed und dann interpretiert, ob dies nun eine Variablendeklaration
          oder ein Befehl ist, spielt Perl eigentlich keine Rolle.

          P.S. ich teile so viel Kritik aus, da sollte man durchaus auch Kritik einstecken können ;-)

          Das musst du positiv sehen! Kritik ist da um zu lernen, also bitte "schlag auch bei mir
          rein" ;)

          Viele Grüsse

          Philipp

          1. Wieso? - Storable liest genauso die ganze Datei ein, wie du auch über deinen Lösungsweg
            machen musst. Zudem hat Storable ein C-Backend, was es doch ganz schön schnell macht,
            eval hingegen (wohl auch auf C basierend...) muss den eingelesenen String zuerst
            parsen, dann wird er in einen Perl-Parsed-Tree umgewandelt und dann interpretiert, wie
            das mit allen Perlprogrammen geschieht; das macht die Sache eben langsam.

            Das eval langsam ist verkündige ich ja auch schon seit Jahren (vor allem bei JS).

            Was ich meinte ist, du kannst mit Storable nur ein Variabel auf einmal speichern und lesen.
            Und was ich nur im 1.Posting andeutete, verwende ich Data::Dumper und DB_File genmeinsam, um komplexere Strukturen abzuspeichern und zu laden. Also die einzelnen Strukturen des Hash per Dumper speichern und mit eval einlesen. Das ist herrlich flexibel (und ich hoffe bei meinen Datenbanken noch halbwegs performant)

            Ich hab grad in der MLDBM Doku gelesen, das auch dort mit Data::Dumper gearbeitet wird, allerdings Storable (wegen Geschwindigkeit) empfohlen wird.

            eval sollte aber keine Probleme machen, da der Inhalt ja nicht ausgeführt wird, sondern der output von Data::Dumper, der (oder verläßt mich da meine Vorstellungskraft) keinen Code enthält, sondern Strings, die die Datenstruktur darstellt.

            Auch Strings gehören zu Perl und werden interpretiert, genauso wie Befehle. Natürlich
            gibt es bei Data::Dumper nicht viel "auszuführen", dennoch wird der Code (und _das_ ist
            er dennoch) zuerst geparsed und dann interpretiert, ob dies nun eine Variablendeklaration
            oder ein Befehl ist, spielt Perl eigentlich keine Rolle.

            Ja aber, der "code" wird ja erst erzeugt von Dumper und dann geparst.

            use Data::Dumper;
            my $test ="system dir";
            my $VAR1 = Dumper($test);
            eval $VAR1;

            Da passiert nichts.

            Struppi.

            1. Halihallo Struppi

              Was ich meinte ist, du kannst mit Storable nur ein Variabel auf einmal speichern und lesen.

              Man braucht _nie_ mehr ;)

              Und was ich nur im 1.Posting andeutete, verwende ich Data::Dumper und DB_File genmeinsam, um komplexere Strukturen abzuspeichern und zu laden.

              Komplexität hat _gar nichts_ mit der "Anzahl von Variablen" zu tun. Ich kann dir eine
              wunderschön Komplexe Datenstruktur in eine Variable packen... Im Gegenteil, es ist eben
              nicht komplex und meistens unsauber programmiert, wenn man mehrere "Variablen" benötigt.
              Du kannst mit einer Referenz auf einen Skalar, Array, Hash, Code, ... alles abbilden.
              Das einzige, was Storable von Data::Dumper in unserem Sinne unterscheidet ist, dass
              Storable eine Referenz als Input _erwartet_, bei Data::Dumper bist du da frei; dies als
              Vorteil zu sehen ist jedoch falsch.

              Also die einzelnen Strukturen des Hash per Dumper speichern und mit eval einlesen. Das ist herrlich flexibel (und ich hoffe bei meinen Datenbanken noch halbwegs performant)

              Hast du dich wirklich über Storable informiert? - Das kann nämlich mindestens gleich
              viel, ist jedoch noch wesentlich schneller.

              Ich hab grad in der MLDBM Doku gelesen, das auch dort mit Data::Dumper gearbeitet wird, allerdings Storable (wegen Geschwindigkeit) empfohlen wird.

              Aha, die werden sich wohl schon was dabei überlegt haben, oder? ;)

              use Data::Dumper;
              my $test ="system dir";
              my $VAR1 = Dumper($test);
              eval $VAR1;

              Da passiert nichts.

              Eben doch. In $VAR1 hast du dann '$VAR1 = "system dir"' stehen und dieser Ausdruck,
              wird zuerst geparsed, dann in einen Perl-Parsed-Tree umgewandelt und wie ein normales
              Perl-Programm ausgeführt... Klar, dass der Befehl "system dir" nicht ausgeführt wird,
              aber es findet ein allokieren von Speicher für die Variable $VAR1 statt, dann wird
              ihr eine neue String-Konstante zugewiesen.
              Das geschieht zwar auch etwa in Storable, nur dass da nicht der Perl-Parser anspringt und
              versucht ein lauffähiges Script zu interpretieren, sondern ein sehr viel schlankerer
              Code, der einfach gewisse Datenstrukturen einliest und _das_ ist der Geschwindigkeits-
              gewinn (Stell dir vor: Dein eval $VAR1 überprüft gar die Syntax des Dumper outputs und
              versucht den "Perl-Code" zu optimieren! - Bei Storable ist das ziemlich viel einfacher,
              da man es _ausschliesslich_ mit Daten zu tun hat; der Output von Dumper _ist_ ein
              _lauffähiges_ Perl-Programm).

              Naja, ich glaube du hast jetzt verstanden, warum ich so gegen Data::Dumper interveniert
              habe ;)

              Viele Grüsse

              Philipp

    2. Hi,

      erstmal Dank für Deine Antwort. Dem Data::Dumper-Ansatz kann ich leider nix abgewinnen.

      us DB_File;

      my %HASH;

      tie %HASH , 'DB_File', 'datei', O_RDWR|O_CREAT, 0777;

      %HASH{neu}  =  'neuer Eintrag';

      Nur dass das bei mir gerademal gar nicht funktioniert. Ich hab's mit den Beispielen aus der manpage probiert... :-((

      Vielleicht versuch ich's nochmal.

      Grüße

      Andreas

      1. Hi,

        erstmal Dank für Deine Antwort. Dem Data::Dumper-Ansatz kann ich leider nix abgewinnen.

        Ich benutz den als flexiblen DB Ersatz.
        Du musst keine Felder definieren, und kannst alle Strukturen abspeichern und wieder laden.
        Während DB_File halt nur einfache HASH Strukturen zuläßt.

        Es gibt aber noch MLDBM ich weiss aber nicht, warum ich das nicht benutz habe, da muss es irgendeinen Haken geben.

        us DB_File;

        my %HASH;

        tie %HASH , 'DB_File', 'datei', O_RDWR|O_CREAT, 0777;

        %HASH{neu}  =  'neuer Eintrag';

        Nur dass das bei mir gerademal gar nicht funktioniert. Ich hab's mit den Beispielen aus der manpage probiert... :-((

        Was funktioniert nicht?

        DB_File ist nicht in allen Perl Versionen installiert, muss evtl. nach installiert werden.

        Struppi.

        1. N'Abend!

          Ich benutz den als flexiblen DB Ersatz.
          Du musst keine Felder definieren, und kannst alle Strukturen abspeichern und wieder laden.
          Während DB_File halt nur einfache HASH Strukturen zuläßt.

          Die mir ja reichen würden...

          Nur dass das bei mir gerademal gar nicht funktioniert. Ich hab's mit den Beispielen aus der manpage probiert... :-((

          Was funktioniert nicht?

          Sobald ich einen Datensatz geschrieben habe, wird, wenn ich den nächsten schreiben will, immer der komplette erste Datensatz überschrieben.

          Ich kann also immer nur einen Datensatz speichern. :-(

          DB_File ist nicht in allen Perl Versionen installiert, muss evtl. nach installiert werden.

          Doch, ist bei mir installiert, sonst würde mein #! /usr/bin/perl -w ja ganz schön meckern. :-)

          Grüße und gute Nacht

          Andreas

          1. hehe, moin ;-)

            N'Abend!

            Ich benutz den als flexiblen DB Ersatz.
            Du musst keine Felder definieren, und kannst alle Strukturen abspeichern und wieder laden.
            Während DB_File halt nur einfache HASH Strukturen zuläßt.

            Die mir ja reichen würden...

            Nur dass das bei mir gerademal gar nicht funktioniert. Ich hab's mit den Beispielen aus der manpage probiert... :-((

            Was funktioniert nicht?

            Sobald ich einen Datensatz geschrieben habe, wird, wenn ich den nächsten schreiben will, immer der komplette erste Datensatz überschrieben.

            Ich kann also immer nur einen Datensatz speichern. :-(

            Mein erstes Posting war ja voller Fehler (ich hahtte das nur so dahin gehackt), du hast das nicht so übernommmen?

            Wie sieht denn dein Code aus, der nicht funktioniert?

            Struppi.

            1. Schon wieder fast N'Abend!

              Sobald ich einen Datensatz geschrieben habe, wird, wenn ich den nächsten schreiben will, immer der komplette erste Datensatz überschrieben.

              Ich kann also immer nur einen Datensatz speichern. :-(

              Mein erstes Posting war ja voller Fehler (ich hahtte das nur so dahin gehackt), du hast das nicht so übernommmen?

              Ne ne, ich habe nicht Dein Beispiel verwendet, sondern die, die in der manpage zu DB_File waren.

              Und da war es dann so, dass ich zwar einen Datensatz speichern konnte (in einer 12 kB - Datei für 200 Zeichen Text), aber wenn ich dann einen Datensatz anhängen wollte, wurde der erste komplett überschrieben.

              Wie sieht denn dein Code aus, der nicht funktioniert?

              Moment, ich muss mal kurz man DB_File aufrufen...

              use warnings ;
              use strict ;
              use DB_File ;
              use vars qw( %h $k $v ) ;

              unlink "fruit" ;
              tie %h, "DB_File", "fruit", O_RDWR|O_CREAT, 0640, $DB_HASH or die "Cannot open file 'fruit': $!\n";

              Add a few key/value pairs to the file

              $h{"apple"} = "red" ;
              $h{"orange"} = "orange" ;
              $h{"banana"} = "yellow" ;
              $h{"tomato"} = "red" ;

              Check for existence of a key

              print "Banana Exists\n\n" if $h{"banana"} ;

              Delete a key/value pair.

              delete $h{"apple"} ;

              print the contents of the file

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

              untie %h ;

              Das hat auch soweit funktioniert. Aber wenn ich dann erneut die Datenbank aufmachen will und neue Daten speichern will, habe ich die alten verloren.

              Grüße

              Andreas

              1. unlink "fruit" ;

                [..]

                Das hat auch soweit funktioniert. Aber wenn ich dann erneut die Datenbank aufmachen will und neue Daten speichern will, habe ich die alten verloren.

                Logisch, du weist was der Befehl da oben macht?

                Struppi.