bituniverse.com Foren-Übersicht bituniverse.com
Entwickler Forum
 
 FAQFAQ   SuchenSuchen   MitgliederlisteMitgliederliste   BenutzergruppenBenutzergruppen   RegistrierenRegistrieren 
 ProfilProfil   Einloggen, um private Nachrichten zu lesenEinloggen, um private Nachrichten zu lesen   LoginLogin 

Wie kommen meine Daten auf die Platte?

 
Neues Thema eröffnen   Neue Antwort erstellen    bituniverse.com Foren-Übersicht -> Tutorials
Vorheriges Thema anzeigen :: Nächstes Thema anzeigen  
Autor Nachricht
Jens
Administrator


Anmeldedatum: 05.11.2007
Beiträge: 193

BeitragVerfasst am: Fr 09 Nov, 2007 21:25    Titel: Wie kommen meine Daten auf die Platte? Antworten mit Zitat

Kapitel 1 - Vorüberlegungen

Heutzutage wird zwar zur Ablage von Daten in den meißten Fällen zu einer Datenbank gegriffen, ein paar grundlegende Informationen zum Thema Arbeiten mit Dateien können aber trotzdem nicht schaden.

Diese Tutorialserie beschreibt deshalb mehrere Verfahren zur Ablage von Daten in Dateien und versucht diese in einen Kontext zu ihrer Eignung für bestimmte beispielhafte Problemszenarien zu setzen.

Beginnen wir mit ein paar Vorüberlegungen:

Grundsätzlich geht es bei der Arbeit mit Daten immer um die folgenden Operationen.

  • Speichern eines Datensatzes
  • Lesen eines Datensatzes
  • Verändern eines Datensatzes
  • Löschen eines Datensatzes
  • Finden eines Datensatzes

Zur Durchführung dieser Operationen muss zuerst grundlegend über den Lagerort entschieden werden. Das hier nicht ein Verfahren für alle möglichen Operationen die optimalste Leistung bringen kann, dürfte jedem einleuchten.

Eine Wahl über das geeignete Verfahren kann also nur dann getroffen werden, wenn die genauen Anforderungen bekannt sind.

Nun führen ja bekanntlich auch viele Wege nach Rom, und nicht jeder ist für jeden Zweck der optimalste. Stellen wir uns vor, ich wolle mit einem Container voll Waren nach Rom, dann währe mein gewählter Weg wohl am ehesten ein LKW und die Autobahn, während ich als Alleinreisender Geschäftsmann wohl eher in den Flieger steigen würde.

Gleiches gilt nun auch für die Arbeit mit Dateien. Je nach Art und Menge der geplanten Operationen, sowie nach der zu behandelnden Datenmenge unterscheidet ist das geeignete Verfahren für die Behandlung der Daten nicht unbedingt immer identisch.

Neben dem Wissen um die möglichen Verfahren, müssen also folgende Kriterien Berücksichtigung finden:

  • Die Art der durchzuführenden Operationen
  • Die Anzahl an zu tätigenden Operationen pro Zeiteinheit
  • Die relative Häufigkeit der einzelnen Operationsarten
  • Die bei den Operationen absolut zu bewegende Datenmenge
  • Die Datenmengen pro Operation und Operationsart

Das hört sich aber natürlich wieder wilder an, als es ist. Konkret bedeutet dies nur, daß vor der Entscheidung für ein Verfahren die gewünschte Leistungsfähigkeit formuliert werden muss. Will ich zum Beispiel ein kleines Gästebuch entwickeln, wo pro Tag vielleicht zwei Beiträge geschrieben werden, und höchstens 20 Abrufe pro Tag stattfinden (sagen wir mal je 5 auf einmal), lässt sich ein Teil der obigen Kriterien leicht sammeln:

Zuerst einmal sagt die Rechtslage, daß ein Webmaster für alle Inhalte seiner Seite nach Kenntnisnahme mitverantwortlich ist, auch wenn er sie nicht selbst verfasst hat. Schon rein aus rechtlicher sich müssen wir also neben Schreiben und Lesen auch Editieren und Löschen unterstützen. Punkt 1) kann man also mit "alle 4" beantworten.

Die Anzahl der zu tätigenden Operationen liest sich schon aus den Vorbedingungen heraus:

  • 100x Lesen pro Tag
  • 2x Schreiben pro Tag
  • <=2x Editieren pro Tag
  • <=2x Löschen pro Tag
  • <=104x Finden pro Tag

Ein Eintragschreiber schreibt ja nun normalerweise keine Buch. Der Text wird im Schnitt wohl unter 2kb pro Eintrag bleiben. Kalkulieren wir nun sagen wir mal mit einer (künstlich hoch gewählten) Lebensdauer von 5 Jahren, dann ergibt sich eine maximale Datenmenge von 3.5mb.

Aber halt. Zu einem Gästebucheintrag gehört ja mehr als nur der Text. Zu jedem Eintrag gehört auch noch ein Name des Authors (z.B. max 15 Byte), sowie Datum und Uhrzeit (wieder 15 Byte). Insgesamt landen wir also bei 3.6mb. Damit wäre der Punkt 4) auch vom Tisch.

Nun fehlt noch Punkt 5), aber auch das ergibt sich direkt aus den schon ermittelten Werten

  • Lesen muss man pro Operation maximal 10kb
  • Schreiben muss man pro Operation maximal 2kb
  • Editieren muss man pro Operation maximal 2kb
  • Löschen muss man ebenfalls maximal 2kb auf einmal
  • Und durchsuchen muss man maximal 3.6mb

Auf Basis dieser Informationen könnte nun ein geeignete Verfahren gewählt werden. In den folgenden Kapiteln werde ich nun einige Verfahren an Beispielen beschreiben, wie Daten auf die Festplatte gebracht werden können. Anschließend werde ich noch für einige Beispielhafte Problemszenarien mehrere die Eignung der jeweiligen Verfahren gegenüberstellen.

Gruß Jens

Nach oben
Benutzer-Profile anzeigen Private Nachricht senden
Jens
Administrator


Anmeldedatum: 05.11.2007
Beiträge: 193

BeitragVerfasst am: So 11 Nov, 2007 01:05    Titel: Antworten mit Zitat

Kapitel 2 - eine Zeile pro Datensatz

Ein gängiges Modell zur Organisation von Daten in einer Datei ist die Ablage eines zusammenhängenden Datensatzes in einer Zeile. Die Einzelelemente des Datensatzes werden dabei für gewöhnlich von einem möglichst seltenen Trennzeichen voneinander abgetrennt.

Nehmen wir an, unser Datensatz bestünde aus den folgenden Einzelteilen:

  • Datum - Unix-Timestamp
  • Inhalt - ein einfacher Text

Als Trennzeichen verwenden wir zwei aufeinander folgende Prozentzeichen.

Code:
  1. 1194730024%%Dies ist der erste Inhalt
  2. 1194729021%%Dies ist der zweite Inhalt
  3. 1194723027%%Dies ist der dritte Inhalt

Nun müssen wir noch den Code für die Eingangs genannten Operationen erstellen.

  • Speichern eines Datensatzes
  • Lesen eines Datensatzes
  • Verändern eines Datensatzes
  • Löschen eines Datensatzes
  • Finden eines Datensatzes

Eine mögliche Realisierung wäre diese:

Php:
  1. <?php
  2.  
  3.   /**
  4.    * Diese Funktion hängt einen neuen Datensatz an die Datei an.
  5.    * @param String $dateiname Der Name der Datei mit den Daten
  6.    * @param integer $datum Ein Unixtimestamp
  7.    * @param String $inhalt Der Inhalt dieses Datensatzes
  8.    */
  9.   function schreibe($dateiname, $datum, $inhalt)
  10.   {
  11.     // Prüfe Datei auf Existenz
  12.     if(!file_exists($dateiname))
  13.     {
  14.       if(!is_writable(dirname($dateiname)))
  15.         return trigger_error(E_USER_ERROR,'Die Datendatei existiert nicht und kann auch nicht angelegt werden.');
  16.     }else
  17.     {
  18.       // prüfe Dateiname auf Beschreibbarkeit    
  19.       if(!is_writable($dateiname))
  20.         return trigger_error(E_USER_ERROR,'Die Datendatei existiert, kann aber nicht beschrieben werden.');    
  21.     }
  22.      
  23.     // Öffne Datei. Wenn die Datei noch nicht existiert, wird diese angelegt
  24.     $fd=fopen($dateiname,'a+');
  25.    
  26.     // Überprüfe ob das Öffnen geklappt hat
  27.     if(!is_resource($fd))
  28.       return trigger_error(E_USER_ERROR,'Die Datendatei konnte nicht geöffnet werden.');
  29.    
  30.     // Zeilenumbrüche in $inhalt maskieren:
  31.     $inhalt=addcslashes($inhalt,"\0..\37!@\@\177..\377");
  32.    
  33.     // neue Zeile für die Datei zusammenstellen:
  34.     $neueZeile="{$datum}%%{$inhalt}\r\n";
  35.    
  36.     // neue Zeile ans Dateiende hängen
  37.     fputs($fd,$neueZeile);
  38.    
  39.     fclose($fd);  
  40.   }
  41.  
  42.   /**
  43.    * Diese Funktion liefert einen Datensatz zurück oder False falls dieser nicht existiert.
  44.    * @param String $dateiname Der Name der Datei mit den Daten
  45.    * @param integer $ordnungsnummer Die Ordnungsnummer des Datensatzes
  46.    * @return array/false
  47.    */
  48.   function lese($dateiname,$ordnungsnummer)
  49.   {
  50.     // Prüfe auf Existenz und Lesbarkeit der Datendatei
  51.     if(!(file_exists($dateiname) && is_readable($dateiname)))
  52.       return trigger_error(E_USER_ERROR,'Die Datendatei ist nicht lesbar!');
  53.    
  54.     // Datei öffnen.
  55.     $fd=fopen($dateiname,'r');
  56.    
  57.     // Überprüfe ob das öffnen geklappt hat
  58.     if(!is_resource($fd))
  59.       return trigger_error(E_USER_ERROR,'Die Datendatei konnte nicht geöffnet werden.');
  60.      
  61.     for($i=0,$zeile='';!feof($fd);$zeile=fgets($fd),$i++)
  62.     {
  63.       // Zeilenumbruch abschneiden
  64.       $zeile=rtrim($zeile);
  65.      
  66.       // $datum und $inhalt extrahieren
  67.       list($datum,$inhalt)=explode('%%',$zeile);
  68.      
  69.       // Zeilenumbrüche in $inhalt wieder herstellen
  70.       $inhalt=stripcslashes($inhalt);
  71.       if($i==$ordnungsnummer)
  72.       {
  73.         fclose($fd);
  74.         return array('datum'=>$datum,
  75.                      'inhalt'=>$inhalt);
  76.       }
  77.     }
  78.     fclose($fd);
  79.     return false;
  80.   }
  81.  
  82.   /**
  83.    * Diese Funktion ersetzt einen durch die Ordnungsnummer identifizierten Datensatz
  84.    * @param String $dateiname Der Name der Datei mit den Daten
  85.    * @param integer $ordnungsnummer Die Ordnungsnummer des Datensatzes
  86.    * @param integer $datum Ein neuer Unixtimestamp
  87.    * @param String $inhalt Der neue Inhalt dieses Datensatzes
  88.    */
  89.   function ersetze($dateiname,$ordnungsnummer,$datum,$inhalt)
  90.   {
  91.     // temporäre Datei erstellen:
  92.     $tmpFile=tempnam(sys_get_temp_dir(),'datafile_');
  93.    
  94.     // temporäre Datei öffnen:
  95.     $tmpFd=fopen($tmpFile,'a+');
  96.  
  97.     // Überprüfe ob das öffnen geklappt hat
  98.     if(!is_resource($tmpFd))
  99.       return trigger_error(E_USER_ERROR,'Die temporäre Datendatei konnte nicht geöffnet werden.');
  100.  
  101.     // Prüfe auf Existenz und Lesbarkeit der Datendatei
  102.     if(!(file_exists($dateiname) && is_readable($dateiname)))
  103.       return trigger_error(E_USER_ERROR,'Die Datendatei ist nicht lesbar!');
  104.    
  105.     // Datei öffnen.
  106.     $fd=fopen($dateiname,'r');
  107.    
  108.     // Überprüfe ob das öffnen geklappt hat
  109.     if(!is_resource($fd))
  110.       return trigger_error(E_USER_ERROR,'Die Datendatei konnte nicht geöffnet werden.');
  111.  
  112.  
  113.     for($i=0,$zeile='';!feof($fd);$zeile=fgets($fd),$i++)
  114.     {
  115.       // Zeilenumbruch abschneiden
  116.       $zeile=rtrim($zeile);
  117.      
  118.       // $datum und $inhalt extrahieren
  119.       list($datumAlt,$inhaltAlt)=explode('%%',$zeile);
  120.      
  121.       // Zeilenumbrüche in $inhalt wieder herstellen
  122.       $inhaltAlt=stripcslashes($inhaltAlt);
  123.      
  124.       if($i==$ordnungsnummer)
  125.       {
  126.         // übernehme neuen Datensatz
  127.         $inhaltNeu=$inhalt;
  128.         $datumNeu=$datum;
  129.       }else
  130.       {
  131.         // übernehme alten Datensatz
  132.         $inhaltNeu=$inhaltAlt;
  133.         $datumNeu=$datumAlt;
  134.       }
  135.      
  136.  
  137.       // Zeilenumbrüche in $inhalt maskieren:
  138.       $inhaltNeu=addcslashes($inhaltNeu,"\0..\37!@\@\177..\377");
  139.    
  140.       // neue Zeile für die Datei zusammenstellen:
  141.       $neueZeile="{$datumNeu}%%{$inhaltNeu}\r\n";
  142.      
  143.       // Neue Werte in temporäre Datei schreiben:      
  144.       fputs($tmpFd,$neueZeile);      
  145.     }
  146.     fclose($fd);
  147.     fclose($tmpFd);
  148.     // und jetzt muss die Originaldatei noch durch die Temporäre ersetzt werden
  149.     unlink($dateiname);
  150.     rename($tmpFile,$dateiname);
  151.   }
  152.    
  153.   /**
  154.    * Diese Funktion löscht einen durch die Ordnungsnummer identifizierten Datensatz
  155.    * @param String $dateiname Der Name der Datei mit den Daten
  156.    * @param integer $ordnungsnummer Die Ordnungsnummer des Datensatzes
  157.    */
  158.   function loesche($dateiname,$ordnungsnummer)
  159.   {
  160.     // temporäre Datei erstellen:
  161.     $tmpFile=tempnam(sys_get_temp_dir(),'datafile_');
  162.    
  163.     // temporäre Datei öffnen:
  164.     $tmpFd=fopen($tmpFile,'a+');
  165.  
  166.     // Überprüfe ob das öffnen geklappt hat
  167.     if(!is_resource($tmpFd))
  168.       return trigger_error(E_USER_ERROR,'Die temporäre Datendatei konnte nicht geöffnet werden.');
  169.  
  170.     // Prüfe auf Existenz und Lesbarkeit der Datendatei
  171.     if(!(file_exists($dateiname) && is_readable($dateiname)))
  172.       return trigger_error(E_USER_ERROR,'Die Datendatei ist nicht lesbar!');
  173.    
  174.     // Datei öffnen.
  175.     $fd=fopen($dateiname,'r');
  176.    
  177.     // Überprüfe ob das öffnen geklappt hat
  178.     if(!is_resource($fd))
  179.       return trigger_error(E_USER_ERROR,'Die Datendatei konnte nicht geöffnet werden.');
  180.  
  181.  
  182.     for($i=0,$zeile='';!feof($fd);$zeile=fgets($fd),$i++)
  183.     {
  184.       // Zeilenumbruch abschneiden
  185.       $zeile=rtrim($zeile);
  186.      
  187.       // $datum und $inhalt extrahieren
  188.       list($datumAlt,$inhaltAlt)=explode('%%',$zeile);
  189.  
  190.       // handelt es sich um eine Zeile, die erhalten bleiben soll?      
  191.       if($i!=$ordnungsnummer)
  192.       {
  193.         // neue Zeile für die Datei zusammenstellen:
  194.         $neueZeile="{$datumAlt}%%{$inhaltAlt}\r\n";
  195.      
  196.         // Werte in temporäre Datei schreiben:      
  197.         fputs($tmpFd,$neueZeile);      
  198.       }
  199.     }
  200.     fclose($fd);
  201.     fclose($tmpFd);
  202.    
  203.     // und jetzt muss die Originaldatei noch durch die Temporäre ersetzt werden
  204.     unlink($dateiname);
  205.     rename($tmpFile,$dateiname);
  206.   }
  207.    
  208.   /**
  209.    * Diese Funktion sucht einen Datensatz in einer Datei und liefert seine Nr zurück oder false.
  210.    * @param String $dateiname Der Name der Datei mit den Daten  
  211.    * @param String $wo Wo soll gesucht werden ('datum' oder 'inhalt')
  212.    * @param Mixed  $was was soll gesucht werden
  213.    * @return Integer/false
  214.    */
  215.   function suche($dateiname,$wo,$was)
  216.   {
  217.     // Prüfe auf Existenz und Lesbarkeit der Datendatei
  218.     if(!(file_exists($dateiname) && is_readable($dateiname)))
  219.       return trigger_error(E_USER_ERROR,'Die Datendatei ist nicht lesbar!');
  220.    
  221.     // Datei öffnen.
  222.     $fd=fopen($dateiname,'r');
  223.    
  224.     // Überprüfe ob das öffnen geklappt hat
  225.     if(!is_resource($fd))
  226.       return trigger_error(E_USER_ERROR,'Die Datendatei konnte nicht geöffnet werden.');
  227.      
  228.     for($i=0,$zeile='';!feof($fd);$zeile=fgets($fd),$i++)
  229.     {
  230.       // Zeilenumbruch abschneiden
  231.       $zeile=rtrim($zeile);
  232.      
  233.       // $datum und $inhalt extrahieren
  234.       list($datum,$inhalt)=explode('%%',$zeile);
  235.      
  236.       // Zeilenumbrüche in $inhalt wieder herstellen
  237.       $inhalt=stripcslashes($inhalt);
  238.      
  239.       // Wenn gesuchtes Element, dann
  240.       if(${$wo}==$was)
  241.       {
  242.         // Dateihandle killen
  243.         fclose($fd);
  244.         // Ordnungsnummer zurück liefern
  245.         return $i;
  246.       }
  247.     }
  248.     fclose($fd);
  249.     return false;
  250.  
  251.   }
  252. ?>

Vorteil dieser Variante ist die extrem leichte Implementierbarkeit. Wie leicht ersichtlich ist, basieren alle Operationen ausschließlich auf der Lese und der Schreib-Operation. Weiterer Vorteil ist die sehr einfache (und performante) Schreiboperation für neue Datensätze.

Nachteil ist hier allerdings, daß für das Verändern oder Löschen eines Datensatzes schlimmsten falls die komplette Datei neu geschrieben werden muss. Ein ähnlicher Aufwand für die Suche nach einem konkreten Datensatz an. Auch hier muss potentiell die komplette Datei durchsucht werden.

Ein weiterer Nachteil ist, daß das gewählte Trennzeichen nicht in einem der Datenfelder stehen darf. Ein Inhalt von %% würde uns den Datensatz zerschießen. Außerdem kann diese Variante keine Binärdaten verdauen.



Zuletzt bearbeitet von Jens am Do 03 Jan, 2008 12:25, insgesamt 2-mal bearbeitet
Nach oben
Benutzer-Profile anzeigen Private Nachricht senden
Jens
Administrator


Anmeldedatum: 05.11.2007
Beiträge: 193

BeitragVerfasst am: So 11 Nov, 2007 18:48    Titel: Antworten mit Zitat

Kapitel 3 - CSV

Nun sind wir ja nicht die ersten, die Daten in einer Datei ablegen wollen. Einer der Quasi-Standards ist die Ablage von Inhalten im sogenannten CSV-Format. Das Akronym CSV steht für Comma Separated Value, also für Kommata-getrennte-Werte. In der Basisform ist das nichts anderes, als das Modell aus Kapitel 2 mit einem Komma als Trennzeichen. Da Kommata allerdings die dumme Angewohnheit haben, in Texten recht häufig vor zu kommen, wurde das Konzept um Anführungszeichen um Strings erweitert. Kurz gesagt, werden alle nicht numerischen Werte in Anführungszeichen eingefasst, und im Text vorkommende Anführungszeichen werden entsprechend mittels eines Backslash ausmaskiert.

Zusätzlicher Vorteil dieser Methode ist, daß PHP mit den Funktion fgetcsv() und fputcsv() schon entsprechende Funktionen zum Handling je eines Datensatzes mit bringt. Grundsätzlich gilt, daß native PHP-Funktionen in den meißten Fällen schneller sind, als in PHP implementierter Code. Insofern schlägt diese Variante das im zweiten Kapitel vorgestellte Modell in der Performance um ein paar Prozent.

Definieren wir wieder den Code für die eingangs definierten Operationen:

  • Speichern eines Datensatzes
  • Lesen eines Datensatzes
  • Verändern eines Datensatzes
  • Löschen eines Datensatzes
  • Finden eines Datensatzes


Php:
  1. <?php
  2.   /**
  3.    * Diese Funktion hängt einen neuen Datensatz an die Datei an.
  4.    * @param String $dateiname Der Name der Datei mit den Daten
  5.    * @param integer $datum Ein Unixtimestamp
  6.    * @param String $inhalt Der Inhalt dieses Datensatzes
  7.    */
  8.   function schreibe($dateiname, $datum, $inhalt)
  9.   {
  10.     // Prüfe Datei auf Existenz
  11.     if(!file_exists($dateiname))
  12.     {
  13.       if(!is_writable(dirname($dateiname)))
  14.         return trigger_error(E_USER_ERROR,'Die Datendatei existiert nicht und kann auch nicht angelegt werden.');
  15.     }else
  16.     {
  17.       // prüfe Dateiname auf Beschreibbarkeit    
  18.       if(!is_writable($dateiname))
  19.         return trigger_error(E_USER_ERROR,'Die Datendatei existiert, kann aber nicht beschrieben werden.');    
  20.     }
  21.      
  22.     // Öffne Datei. Wenn die Datei noch nicht existiert, wird diese angelegt
  23.     $fd=fopen($dateiname,'a+');
  24.    
  25.     // Überprüfe ob das Öffnen geklappt hat
  26.     if(!is_resource($fd))
  27.       return trigger_error(E_USER_ERROR,'Die Datendatei konnte nicht geöffnet werden.');
  28.  
  29.     // neuen Datensatz an Datei anhängen:    
  30.     fputcsv($fd,array($datum,$inhalt));
  31.    
  32.     // Datei schließen
  33.     fclose($fd);  
  34.   }
  35.  
  36.   /**
  37.    * Diese Funktion liefert einen Datensatz zurück oder False falls dieser nicht existiert.
  38.    * @param String $dateiname Der Name der Datei mit den Daten
  39.    * @param integer $ordnungsnummer Die Ordnungsnummer des Datensatzes
  40.    * @return array/false
  41.    */
  42.   function lese($dateiname,$ordnungsnummer)
  43.   {
  44.     // Prüfe auf Existenz und Lesbarkeit der Datendatei
  45.     if(!(file_exists($dateiname) && is_readable($dateiname)))
  46.       return trigger_error(E_USER_ERROR,'Die Datendatei ist nicht lesbar!');
  47.    
  48.     // Datei öffnen.
  49.     $fd=fopen($dateiname,'r');
  50.    
  51.     // Überprüfe ob das öffnen geklappt hat
  52.     if(!is_resource($fd))
  53.       return trigger_error(E_USER_ERROR,'Die Datendatei konnte nicht geöffnet werden.');
  54.      
  55.     for($i=0,$ds=array();!feof($fd);$ds=fgetcsv($fd),$i++)
  56.     {
  57.    
  58.       // handelt es sich um den gesuchten Datensatz?  
  59.       if($i==$ordnungsnummer)
  60.       {
  61.         // schließe Dateihandle
  62.         fclose($fd);
  63.         // gebe Datensatz zurück
  64.         return array('datum'=>$ds[0],
  65.                      'inhalt'=>$ds[1]);
  66.       }
  67.     }
  68.     // schließe Dateihandle
  69.     fclose($fd);
  70.     // gebe False zurück, da kein passender Datensatz gefunden
  71.     return false;
  72.   }
  73.  
  74.   /**
  75.    * Diese Funktion ersetzt einen durch die Ordnungsnummer identifizierten Datensatz
  76.    * @param String $dateiname Der Name der Datei mit den Daten
  77.    * @param integer $ordnungsnummer Die Ordnungsnummer des Datensatzes
  78.    * @param integer $datum Ein neuer Unixtimestamp
  79.    * @param String $inhalt Der neue Inhalt dieses Datensatzes
  80.    */
  81.   function ersetze($dateiname,$ordnungsnummer,$datum,$inhalt)
  82.   {
  83.     // temporäre Datei erstellen:
  84.     $tmpFile=tempnam(sys_get_temp_dir(),'datafile_');
  85.    
  86.     // temporäre Datei öffnen:
  87.     $tmpFd=fopen($tmpFile,'a+');
  88.  
  89.     // Überprüfe ob das öffnen geklappt hat
  90.     if(!is_resource($tmpFd))
  91.       return trigger_error(E_USER_ERROR,'Die temporäre Datendatei konnte nicht geöffnet werden.');
  92.  
  93.     // Prüfe auf Existenz und Lesbarkeit der Datendatei
  94.     if(!(file_exists($dateiname) && is_readable($dateiname)))
  95.       return trigger_error(E_USER_ERROR,'Die Datendatei ist nicht lesbar!');
  96.    
  97.     // Datei öffnen.
  98.     $fd=fopen($dateiname,'r');
  99.    
  100.     // Überprüfe ob das öffnen geklappt hat
  101.     if(!is_resource($fd))
  102.       return trigger_error(E_USER_ERROR,'Die Datendatei konnte nicht geöffnet werden.');
  103.  
  104.  
  105.     for($i=0,$ds=array();!feof($fd);$ds=fgetcsv($fd),$i++)
  106.     {
  107.       // muss der Datensatz ersetzt werden?
  108.       if($i==$ordnungsnummer)
  109.       {
  110.         // übernehme neuen Datensatz
  111.         $ds=array($datum,$inhalt);
  112.       }
  113.      
  114.       // Neue Werte in temporäre Datei schreiben:      
  115.       fputcsv($tmpFd,$ds);
  116.      
  117.     }
  118.     // Dateien wieder schließen
  119.     fclose($fd);
  120.     fclose($tmpFd);
  121.     // und jetzt muss die Originaldatei noch durch die Temporäre ersetzt werden
  122.     unlink($dateiname);
  123.     rename($tmpFile,$dateiname);
  124.   }
  125.    
  126.   /**
  127.    * Diese Funktion löscht einen durch die Ordnungsnummer identifizierten Datensatz
  128.    * @param String $dateiname Der Name der Datei mit den Daten
  129.    * @param integer $ordnungsnummer Die Ordnungsnummer des Datensatzes
  130.    */
  131.   function loesche($dateiname,$ordnungsnummer)
  132.   {
  133.     // temporäre Datei erstellen:
  134.     $tmpFile=tempnam(sys_get_temp_dir(),'datafile_');
  135.    
  136.     // temporäre Datei öffnen:
  137.     $tmpFd=fopen($tmpFile,'a+');
  138.  
  139.     // Überprüfe ob das öffnen geklappt hat
  140.     if(!is_resource($tmpFd))
  141.       return trigger_error(E_USER_ERROR,'Die temporäre Datendatei konnte nicht geöffnet werden.');
  142.  
  143.     // Prüfe auf Existenz und Lesbarkeit der Datendatei
  144.     if(!(file_exists($dateiname) && is_readable($dateiname)))
  145.       return trigger_error(E_USER_ERROR,'Die Datendatei ist nicht lesbar!');
  146.    
  147.     // Datei öffnen.
  148.     $fd=fopen($dateiname,'r');
  149.    
  150.     // Überprüfe ob das öffnen geklappt hat
  151.     if(!is_resource($fd))
  152.       return trigger_error(E_USER_ERROR,'Die Datendatei konnte nicht geöffnet werden.');
  153.  
  154.  
  155.     for($i=0,$ds=array();!feof($fd);$ds=fgetcsv($fd),$i++)
  156.     {
  157.       // handelt es sich um eine Zeile, die erhalten bleiben soll?      
  158.       if($i!=$ordnungsnummer)
  159.       {
  160.         // Neue Werte in temporäre Datei schreiben:      
  161.         fputcsv($tmpFd,$ds);
  162.       }
  163.     }
  164.     // Dateihandles schließen
  165.     fclose($fd);
  166.     fclose($tmpFd);
  167.    
  168.     // und jetzt muss die Originaldatei noch durch die Temporäre ersetzt werden
  169.     unlink($dateiname);
  170.     rename($tmpFile,$dateiname);
  171.   }
  172.    
  173.   /**
  174.    * Diese Funktion sucht einen Datensatz in einer Datei und liefert seine Nr zurück oder false.
  175.    * @param String $dateiname Der Name der Datei mit den Daten  
  176.    * @param String $wo Wo soll gesucht werden ('datum' oder 'inhalt')
  177.    * @param Mixed  $was was soll gesucht werden
  178.    * @return Integer/false
  179.    */
  180.   function suche($dateiname,$wo,$was)
  181.   {
  182.     // Prüfe auf Existenz und Lesbarkeit der Datendatei
  183.     if(!(file_exists($dateiname) && is_readable($dateiname)))
  184.       return trigger_error(E_USER_ERROR,'Die Datendatei ist nicht lesbar!');
  185.    
  186.     // Datei öffnen.
  187.     $fd=fopen($dateiname,'r');
  188.    
  189.     // Überprüfe ob das öffnen geklappt hat
  190.     if(!is_resource($fd))
  191.       return trigger_error(E_USER_ERROR,'Die Datendatei konnte nicht geöffnet werden.');
  192.  
  193.     // $wo in Ordnungsnummer übersetzen:
  194.     $translation=array('datum'=>0,'inhalt'=>1);
  195.     if(!isset($translation[$wo]))
  196.       return trigger_error(E_USER_ERROR,'Der Suchschlüssel existiert nicht.');
  197.      
  198.     for($i=0,$ds=array();!feof($fd);$ds=fgetcsv($fd),$i++)
  199.     {
  200.       // Wenn gesuchtes Element, dann
  201.       if($ds[$translation[$wo]]==$was)
  202.       {
  203.         // Dateihandle killen
  204.         fclose($fd);
  205.         // Ordnungsnummer zurück liefern
  206.         return $i;
  207.       }
  208.     }
  209.     fclose($fd);
  210.     return false;
  211.  
  212.   }
  213. ?>

Wie man sieht, ist diese Variante schon ein Stück weniger komplex. Außerdem behebt sie einige Probleme des Eigenbaus aus Kapitel zwei. Hier hätten wir keine Probleme, das Trennzeichen in den Inhalt eines Datensatzes zu schreiben.

Im prinzipiellen Aufbau ähneln sich beide Methoden aber noch ungemein. Auch hier haben wir also extrem performante Schreiboperationen, unser Aufwand für Ändern und Löschen ist aber nach wie vor recht hoch. Hinzu kommt natürlich auch hier die Gefahr von Race-Conditions und Deadlocks.

Diese Methode eignet sich allerdings sehr gut, wenn nicht nur PHP auf den Datenspeicher zugreifen soll, da es sich bei CSV um ein standardisiertes Format handelt.

Gruß Jens



Zuletzt bearbeitet von Jens am Do 03 Jan, 2008 12:24, insgesamt 2-mal bearbeitet
Nach oben
Benutzer-Profile anzeigen Private Nachricht senden
Jens
Administrator


Anmeldedatum: 05.11.2007
Beiträge: 193

BeitragVerfasst am: Do 03 Jan, 2008 12:21    Titel: Antworten mit Zitat

Kapitel 4 - serialisierte Listen

PHP bietet die Möglichkeit, beliebige Variableninhalte als String darzustellen oder aus einer solchen Darstellung zu laden. Der zugehörige Transformationsalgorithmus wird als Serialisieren bezeichnet. Zur Ablage von Daten könnten wir jetzt also beigehen und einfach jeden Datensatz mittels serialize() serialisieren. Beim Laden würden wir dann einfach per unserialize() aus dem String unsere Variable zurück generieren.

Um dies umsetzen zu können, müssten wir jetzt mit einem der obigen Verfahren zumindestens mehrere Strings in eine Datei legen und wie oben gezeigt adressieren.

Aber halt! Wir haben ja gelernt, daß mittels serialize() beliebige Variableninhalte in Strings konvertiert werden können. Warum sollten wir also nicht einfach die ganze Liste (also zum Beispiel ein Array) am Stück betrachten? Da Arrays immer im Speicher gehalten werden, ist die Suche in einem Array um Längen schneller erledigt, als direkt in einer Datei auf der Festplatte. Leider muss auf die Zeit zur Suche natürlich immer die Zeit zum Laden des kompletten Arrays in den Speicher dazu gerechnet werden.

Aber dazu später mehr. Für den Moment konzentrieren wir uns erst einmal auf den Code für unsere Basisoperationen:

  • Speichern eines Datensatzes
  • Lesen eines Datensatzes
  • Verändern eines Datensatzes
  • Löschen eines Datensatzes
  • Finden eines Datensatzes

Grundsätzlich definieren wir alle Operationen ausschließlich auf der Kopie unserer Liste im Speicher. Vor Beginn einer Operation muss unsere Liste also in den Speicher geladen werden und nach einer Operation muss die Liste wieder als Datei ausgeschrieben werden.

Zuerst benötigen wir also die Operationen "ladeListe" und "schreibeListe":
Php:
  1. <?php
  2.   /**
  3.    * Diese Funktion läd eine Liste aus einer Datei.
  4.    * @param String $dateiname Der Name der Datei mit den Daten
  5.    * @return array
  6.    */  
  7.   function ladeListe($dateiname)
  8.   {
  9.     // Prüfe auf Existenz und Lesbarkeit der Datendatei
  10.     if(!(file_exists($dateiname) && is_readable($dateiname)))
  11.       return trigger_error(E_USER_ERROR,'Die Datendatei ist nicht lesbar!');
  12.  
  13.     // kompletten Dateiinhalt in den Speicher lesen:    
  14.     $daten=file_get_contents($dateiname);
  15.  
  16.     // Dateiinhalt in Liste transformieren:
  17.     $daten=unserialize($daten);
  18.  
  19.     if(!is_array($daten))
  20.       return trigger_error(E_USER_ERROR,'Die Datendatei enthält keine Liste!');
  21.  
  22.     return $daten;
  23.   }
  24.  
  25.  
  26.   /**
  27.    * Diese Funktion schreibt unsere Liste in eine Datei.
  28.    * @param String $dateiname Der Name der Datei mit den Daten
  29.    * @param array $liste die Liste
  30.    */
  31.   function schreibeListe($dateiname,$liste)
  32.   {
  33.     // Prüfe Datei auf Existenz
  34.     if(!file_exists($dateiname))
  35.     {
  36.       if(!is_writable(dirname($dateiname)))
  37.         return trigger_error(E_USER_ERROR,'Die Datendatei existiert nicht und kann auch nicht angelegt werden.');
  38.     }else
  39.     {
  40.       // prüfe Dateiname auf Beschreibbarkeit    
  41.       if(!is_writable($dateiname))
  42.         return trigger_error(E_USER_ERROR,'Die Datendatei existiert, kann aber nicht beschrieben werden.');    
  43.     }
  44.  
  45.     // liste zu string transformieren
  46.     $daten=serialize($liste);
  47.  
  48.     // String in Datei ablegen:
  49.     file_put_contents($dateiname,$daten);
  50.   }
  51. ?>

Zusätzlich muss die Liste einmalig für die Datei initialisiert werden, damit die beiden gezeigten Funktionen ihren Dienst tun können:

Php:
  1. <?php
  2.   /**
  3.    * Diese Funktion bereitet eine Datei für die Verwendung als Listenspeicher vor.
  4.    * @param String $dateiname Der Name der Datei mit den Daten
  5.    */
  6.   function initialisiere($dateiname)
  7.   {
  8.     schreibeListe($dateiname,array());
  9.   }
  10. ?>

Und nun kommen wieder die Operationen. Hierbei ist zu beachten, daß die ersten beiden der o.g. Listenoperationen aus den Teiloperationen heraus verlagert werden sollten, falls mehrere dieser Teiloperationen in einem Skript durchgeführt werden sollen.

Php:
  1. <?php
  2.   /**
  3.    * Diese Funktion hängt einen neuen Datensatz an die Datei an.
  4.    * @param String $dateiname Der Name der Datei mit den Daten
  5.    * @param integer $datum Ein Unixtimestamp
  6.    * @param String $inhalt Der Inhalt dieses Datensatzes
  7.    */
  8.   function schreibe($dateiname, $datum, $inhalt)
  9.   {
  10.     // Liste aus Datei laden:
  11.     $liste=ladeListe($dateiname);
  12.     // Liste verändern:
  13.     $liste[]=array($datum,$inhalt);
  14.     // Liste zurück schreiben:
  15.     schreibeListe($dateiname,$liste);
  16.   }
  17.  
  18.   /**
  19.    * Diese Funktion liefert einen Datensatz zurück oder False falls dieser nicht existiert.
  20.    * @param String $dateiname Der Name der Datei mit den Daten
  21.    * @param integer $ordnungsnummer Die Ordnungsnummer des Datensatzes
  22.    * @return array/false
  23.    */
  24.   function lese($dateiname,$ordnungsnummer)
  25.   {
  26.     // Liste aus Datei laden:
  27.     $liste=ladeListe($dateiname);
  28.     // Prüfen ob DS existiert:
  29.     if(!(isset($liste[$ordnungsnummer]) && is_array($liste[$ordnungsnummer])))
  30.       return false;
  31.     // DS zurück geben
  32.     return $liste[$ordnungsnummer];
  33.   }
  34.  
  35.   /**
  36.    * Diese Funktion ersetzt einen durch die Ordnungsnummer identifizierten Datensatz
  37.    * @param String $dateiname Der Name der Datei mit den Daten
  38.    * @param integer $ordnungsnummer Die Ordnungsnummer des Datensatzes
  39.    * @param integer $datum Ein neuer Unixtimestamp
  40.    * @param String $inhalt Der neue Inhalt dieses Datensatzes
  41.    */
  42.   function ersetze($dateiname,$ordnungsnummer,$datum,$inhalt)
  43.   {
  44.     // Liste aus Datei laden:
  45.     $liste=ladeListe($dateiname);
  46.     // Liste verändern:
  47.     $liste[$ordnungsnummer]=array($datum,$inhalt);
  48.     // Liste zurück schreiben:
  49.     schreibeListe($dateiname,$liste);
  50.   }
  51.    
  52.   /**
  53.    * Diese Funktion löscht einen durch die Ordnungsnummer identifizierten Datensatz
  54.    * @param String $dateiname Der Name der Datei mit den Daten
  55.    * @param integer $ordnungsnummer Die Ordnungsnummer des Datensatzes
  56.    */
  57.   function loesche($dateiname,$ordnungsnummer)
  58.   {
  59.     // Liste aus Datei laden:
  60.     $liste=ladeListe($dateiname);
  61.     // Prüfen ob DS existiert:
  62.     if(isset($liste[$ordnungsnummer]))
  63.     {
  64.       // Liste verändern:
  65.       unset($liste[$ordnungsnummer]);
  66.       // Liste zurück schreiben:
  67.       schreibeListe($dateiname,$liste);
  68.     }
  69.   }
  70.    
  71.   /**
  72.    * Diese Funktion sucht einen Datensatz in einer Datei und liefert seine Nr zurück oder false.
  73.    * @param String $dateiname Der Name der Datei mit den Daten  
  74.    * @param String $wo Wo soll gesucht werden ('datum' oder 'inhalt')
  75.    * @param Mixed  $was was soll gesucht werden
  76.    * @return Integer/false
  77.    */
  78.   function suche($dateiname,$wo,$was)
  79.   {
  80.     // $wo in Ordnungsnummer übersetzen:
  81.     $translation=array('datum'=>0,'inhalt'=>1);
  82.     if(!isset($translation[$wo]))
  83.       return trigger_error(E_USER_ERROR,'Der Suchschlüssel existiert nicht.');
  84.  
  85.     // Liste aus Datei holen
  86.     $liste=ladeListe($dateiname);
  87.  
  88.     // Ordnungsnummer finden:
  89.     foreach($liste as $i=>$ds)    
  90.       if($ds[$translation[$wo]]==$was)
  91.         return $i;
  92.     // wenn wir hier ankommen wurde kein passender DS gefunden
  93.     return false;
  94.   }
  95. ?>

Wie man sehen kann, sind die einzelnen Operationen relativ übersichtlich geraten. Hauptursache dafür sind die vielfältigen Methoden zum Array-Handling, die PHP sowieso schon mitbringt. Dieses Maß an Übersicht bedeutet aber nicht, daß die gezeigten Operationen sonderlich performant wären. Stellen wir uns nur einmal vor, unsere Liste hätte mehrere tausend Einträge, so müssten wir diese Einträge ja immer zwei mal alle betrachten, bevor und nachdem wir unsere Datensatzoperationen überhaupt angehen können.

Daraus resultiert in fast schon zwingender Logik, daß sich die hier vorgestellte Variante hauptsächlich dann eignet, wenn Daten a) in überschaubarerer Menge vorliegen, und sich b) nur selten verändern. Zusätzlicher Nachteil dieser Methode ist, daß keine andere Sprache das Stringformat im Datenspeicher versteht. Soll also aus Projekten in anderen Sprachen auf die selben Daten zugegriffen werden, dann macht diese Methode wenig Sinn.

Eine OOP-Version dieser Methode wurde z.B. in diesem Codeschnipsel verwendet.

Nach oben
Benutzer-Profile anzeigen Private Nachricht senden
Beiträge der letzten Zeit anzeigen:   
Neues Thema eröffnen   Neue Antwort erstellen    bituniverse.com Foren-Übersicht -> Tutorials Alle Zeiten sind GMT + 1 Stunde
Seite 1 von 1

 
Gehe zu:  
Du kannst keine Beiträge in dieses Forum schreiben.
Du kannst auf Beiträge in diesem Forum nicht antworten.
Du kannst deine Beiträge in diesem Forum nicht bearbeiten.
Du kannst deine Beiträge in diesem Forum nicht löschen.
Du kannst an Umfragen in diesem Forum nicht mitmachen.


Powered by phpBB © 2001, 2005 phpBB Group
Deutsche Übersetzung von phpBB2.de
Powered by WebRing.