speedy: Wieder mal ne Textdatenbank

Hi Forumler,

ich habe schon mal so etwas ähnliches gepostet, nun wird meine Frage aber konkreter. Ich habe eine kleine Textdatenbank mit folgendem Schema:

FELD1,FELD2,FELD3,FELD4...
WERT1.1,WERT2.1,WERT3.1,WERT4.1...
WERT1.2,WERT2.2,WERT3.2,WERT4.2...
usw. (Die Trennzeichen sind hier mal als Komma dargestellt).

Meine Frage ist, wie kann ich diese Textdatenbank am sinnvollsten (das ist eine Mischung aus Leistungseffizienz und einfacher Programmierung) in Perl verarbeiten.
Bis jetzt habe ich immer die Datenbank zeilenweise in eine Liste gelesen, wenn nötig anhand einer ID den gewünschen Satz identifiziert und das ganze in ein Hash mit Feldnamen und Werten geschrieben. Das ist sehr umständlich geproggt und sicher auch nicht besonders effizient.
Jetzt würde ich gerne die gesamte Datenbank (die Dateien sind nicht besonders gross) im Zugriff haben und eine Funktion schreiben, die bei Angabe der Satznummer und des Feldnamens den entsprechenden Wert zurückliefert. Was mir fehlt ist die passende Datenstruktur.
Ach ja nochwas: Das Script soll mehrere unterschiedliche Datenbanken mit unterschiedlicher Anzahl von Feldern verarbeiten können.

Für jede Hilfe dankbar
  speedy

  1. Hallo Speedy,

    normalerweise nimmt Dir hier niemand die Arbeit ab, aber ich hatte gerade Spaß an der Lösung Deines Problems. Die Programmierung ist nicht sonderlich elegant, aber sie funktioniert.

    Gruß
      Christian Schnagl

    PS: Es fehlen noch Abfragen wie "Feld gefunden?", aber einfache IF-Anweisungen kriegst Du hin. Bei der Feldbezeichnung muß auf Groß/Kleinschreibung geachtet werden!

    #!/usr/bin/perl
    print "Content-type: text/html\n\n";
    open(DB, "<test.db"); @satz = <DB>; close DB;

    print wert(2,"FELD4");  # Datensatz zwei, Feld 4 ausgeben

    sub wert
    {
      $satznr=$_[0];
      $feldname=$_[1];
      $tmp=0;

    chop($satz[0]);

    @felder=split(/,/,$satz[0]);
      foreach(@felder)
      {
        if ($_ eq $feldname) { $feldnr=$tmp; }
        $tmp++;
      }

    chop($satz[$satznr]);
      @wert=split(/,/,$satz[$satznr]);

    return $wert[$feldnr];
    }

    1. normalerweise nimmt Dir hier niemand die Arbeit ab, aber ich hatte gerade Spaß an der Lösung Deines Problems.

      Hi,

      erstmal danke für die Antwort, mal sehen, obs mir weiterhilft. Nur zum Verständnis: Ich will ja auch nicht, daß mir jemand die Arbeit abnimmt. Es reicht ja, wenn man ungefähr nen Ansatz hat, damit man weiterarbeiten kann.

      Die Programmierung ist nicht sonderlich elegant, aber sie funktioniert.

      Siehe "Programmieren mit Perl, OReilly":
      <cite>
      Ein Perlscript ist ein gutes Script wenn es funktioniert.
      </cite>

      CU
      speedy

      1. <cite>
        Ein Perlscript ist ein gutes Script wenn es funktioniert.
        </cite>

        Definiere "funktionieren". :-(((

    2. Tach auch,

      ein kleiner Korrekturvorschlag fuer das Script folgt innerhalb des Scripts.

      Hallo Speedy,

      normalerweise nimmt Dir hier niemand die Arbeit ab, aber ich hatte gerade Spaß an der Lösung Deines Problems. Die Programmierung ist nicht sonderlich elegant, aber sie funktioniert.

      Gruß

      »»   Christian Schnagl

      PS: Es fehlen noch Abfragen wie "Feld gefunden?", aber einfache IF-Anweisungen kriegst Du hin. Bei der Feldbezeichnung muß auf Groß/Kleinschreibung geachtet werden!

      #!/usr/bin/perl
      print "Content-type: text/html\n\n";
      open(DB, "<test.db"); @satz = <DB>; close DB;

      print wert(2,"FELD4");  # Datensatz zwei, Feld 4 ausgeben

      sub wert
      {

      »»   $satznr=$_[0];
      »»   $feldname=$_[1];
      »»   $tmp=0;

      »»   # chop($satz[0]);
      »»   # Es ist besser, an dieser Stelle statt des chop($satz[0]) ein chomp($satz[0]) zu verwenden, da diese Funktion tatsaechlich nur
         # den Zeilentrenner entfernt und nicht (was zwar unwahrscheinlich ist, aber immerhin moeglich) eventuell das letzte Zeichen der
         # Zeile, ergo:

      »»   chomp($satz[0]);

      @felder=split(/,/,$satz[0]);
      »»   foreach(@felder)
      »»   {
      »»     if ($_ eq $feldname) { $feldnr=$tmp; }
      »»     $tmp++;
      »»   }

      »»   chop($satz[$satznr]);
      »»   @wert=split(/,/,$satz[$satznr]);

      »»   return $wert[$feldnr];

      }

  2. Hallo!

    Schau mal bei <../../sfarchiv/1999_4/t09302.htm#a46976>,
    dort habe ich schonmal eine Code dafuer gepostet. Es
    wird dabei die Liste @csv aufgebaut, in der jeder Eintrag
    eine Hashreferenz ist. Jeder dieser Hashs hat als Key
    den Feldnamen und als Wert eben den Wert.

    In der Zeile
      LoadCSV(@csv, "xyz.csv", '') die("Can't read that fuckin' csv file!\n")
    hat der dumme Schwanzabschneider leider alle Pipe-Zeichen rausgefiltert, deshalb sieht etwas duemmlich aus.
    Fuer Deinen Zweck waere der dritte Parameter von LoadCSV ohnehin ein Komma ','.

    Wenn Du eine eindeutige ID fuer jeden Datensatz hast, bietet es sich an, statt der umgebenden Liste einen Hash zu verwenden, damit der Zugriff schneller wird. Bei der Liste musst Du naemlich in einer Schleife den richtigen Datensatz finden, z.B. mit

    sub FindRecordByNumericFieldvalue($$) {
          my ($fieldname, $value) = (shift, shift);
          for (@csv) {
              return $_ if ($_->{$fieldname} == $value);
          }
          return undef;
      }

    $record = FindRecordByNumericFieldvalue('id', 562);

    Wuerde den Datensatz finden, der im Feld "id" den Wert 562 hat. Vorsicht, der Rueckgabewert kann undef sein. Bei Erfolg Zugriff auf die einzelnen Felder mit
      $record->{'FELD2'}

    Wenn Du aber statt @csv einen %csv verwendest, und als Keys jeweils schon den Wert des id-Feldes, und als Value nach wie vor eine Hashreferenz auf den den Datensatz (war das verstaendlich?), dann waere die Funktion einfach

    sub FindRecordByID($) {
          my $id = shift;
          return $csv{$id} undef;
      }

    $record = FindRecordByID(562);

    Aehm ja, wenn Du Fragen hast, musst Du sie stellen.
    So lange, Calocybe

  3. Hi speedy,

    ich habe schon mal so etwas ähnliches gepostet, nun wird meine Frage aber konkreter. Ich habe eine kleine Textdatenbank mit folgendem Schema:

    FELD1,FELD2,FELD3,FELD4...
    WERT1.1,WERT2.1,WERT3.1,WERT4.1...
    WERT1.2,WERT2.2,WERT3.2,WERT4.2...
    usw. (Die Trennzeichen sind hier mal als Komma dargestellt).

    Oder, Du probierst es doch mal mit den entsprechenden Datenbankmodulen: DBI und DBD::CSV.

    Siehe alten Thread: <../../sfarchiv/2000_1/t11615.htm#a58850>

    ciao,
       Jörk

  4. Hi Speedy,

    FELD1,FELD2,FELD3,FELD4...
    WERT1.1,WERT2.1,WERT3.1,WERT4.1...
    WERT1.2,WERT2.2,WERT3.2,WERT4.2...

    Ich hab` mal ein kleines Modul geschrieben (nicht um das Rad neu zu erfinden, sondern um was zu lernen [über Hash of Hashes und Referenzen] ;-), daß solche Textdatenbanken handelt.
    Hier mal die entscheidenden Zeilen (ein bischen umgeschrieben, da ich bei mir mehrere Subroutinen und Referenzen verwende):

    @attribute = split /$delim/, <LINES>; #   $delim enthält den Delimiter
    chomp $attribute[-1];

    foreach $attribut (@attribute) {
       # primkey enthält die Bezeichnung des Attributs, das als Primärschlüssel dient.  
       last if $attribut eq $primkey;
       # enthält Position des Primärykeys innerhalb des durch Delimiter getrennten Datensatzes
       $$position++;
    }

    while(<LINES>) {
       my $fieldnum = 0;
       @record = split /$delim/; # spaltet Zeile in die Felder des Datensatzes auf
       chomp $record[-1];
       foreach $value (@record) {
          # HoH konstruieren:
          # Hash = ( 'Primarykey1' => { 'Attribut1' => 'Wert1',
          #                             'Attribut2' => 'Wert2' }
          #          'Primarykey2' => { 'Attribut1' => 'Wert1',
          #                             'Attribut2' => 'Wert2' }  );
          $relation{$record[$position]}->{$attribute[$fieldnum++]} = $value;
       }
    }

    Aus der Subroutine, die die Datensätze eingelesen hat, habe ich %relation als Referenz zurückgegeben:
    mit $db{$file} = &dbread($file);
        sub &dbread  { [....] return  %relation; }
    bekomme ich dann eine Datenstruktur, die sich mit:
        # Primärschlüsselwert kennzeichnet bestimmten Datensatz
        $value =  $db{$file}->{'Primärschlüsselwert'}->{'Attribut'};
    dereferenzieren lässt.

    Ich hoffe das funktioniert trotz Umschreibens noch einigermaßen (hätte auch vermutlich auch nix gepostet, wenn`s nicht bei mir so ähnlich herumfliegen würde ;-)
    Das Drumherum musst Du natürlich noch ergänzen ;-)

    Gruß AlexBausW

  5. Also erstmal dankeschön an alle. Da hab ich jetzt genügend Material um mir was schönes zu basteln!

    CU
    speedy