PHP/Dateien lesen und schreiben

Aus Mikiwiki
< PHP
Version vom 6. August 2011, 16:48 Uhr von Michi (Diskussion | Beiträge)
(Unterschied) ← Nächstältere Version | Aktuelle Version (Unterschied) | Nächstjüngere Version → (Unterschied)
Wechseln zu: Navigation, Suche

Dateifunktionen

Dateien zu lesen und zu schreiben gehört zu den elementaren Vorgängen. Dazu werden folgende Funktionen verwendet.

Funktion Beschreibung
readfile Liest eine Datei und sendet sie an den Webbrowser.
file Liest die gesamte Datei in ein Array ein. Als Trennzeichen werden die Zeilenumbrüche verwendet, die dabei erhalten bleiben.
fgets Liest von der Position des Dateizeigers aus einer Datei, bis eines der folgenden Ereignisse eintritt.
  • Die angegebene Anzahl Byte ("<laenge>") ist erreicht.
  • Ein Zeilenende (Zeilenumbruch "\n") wurde erreicht.
  • Das Dateiende wurde erreicht.
$var = fgets($handle, <laenge>);
fread Liest Binärdaten aus einer Datei. Die Funktion entspricht fast der Funktion "fgets", interpretiert aber nicht den Zeilenumbruch. Damit wird tatsächlich bis zum Dateiende oder die angegebene Anzahl Byte gelesen; die Funktion kann daher auch für Binärdateien genutzt werden. Das folgende Beispiel liest vier KB aus einer Datei, die vom Handle "$handle" adressiert wird.
$bytestream = fread($handle, 4096);
fopen Öffnet eine Datei oder einen URL unter Angabe eines Dateinamens und eines Attributs. Das zurückgegebene Handle wird von anderen Funktionen verwendet, um auf die Datei zuzugreifen.
$handle = fopen("dateiname", "attribut");

Das Attribut bestimmt, auf welche Art die Datei geöffnet wird.

Attribut Beschreibung
r Öffnet eine Datei zum Lesen und setzt den Dateizeiger auf den Anfang (das erste Zeichen in der Datei).
r+ Öffnet eine Datei zum Lesen und Schreiben und setzt den Dateizeiger auf den Anfang (das erste zeichen in der Datei).
w Öffnet eine Datei zum Schreiben. Wenn die Datei nicht existiert, wird sie angelegt. Wenn die Datei existiert und Daten enthält, so werden diese gelöscht und die Dateilänge auf 0 gesetzt.
w+ Öffnet eine Datei zum Schreiben und Lesen. Wenn die Datei nicht existiert, wird sie angelegt. Wenn die Datei existiert und Daten enthält, so werden diese gelöscht und die Dateilänge auf 0 gesetzt.
a Öffnet eine Datei zum Schreiben. Wenn die Datei nicht existiert, wird sie angelegt. Wenn die Datei existiert, bleibt sie erhalten. Der Dateizeiger steht am Ende der Datei ("a" steht für "append", dt. anhängen).
a+ Öffnet eine Datei zum Schreiben und Lesen. Wenn die Datei nicht existiert, wird sie angelegt. Wenn die Datei existiert, bleibt sie erhalten. Der Dateizeiger steht am Ende der Datei.
fputs
fwrite
Schreibt Daten an die Stelle des Dateizeigers in eine Datei. "fwrite" ist lediglich ein Alias für "fputs". Die Angabe der Länge "<laenge>" ist optional; wird sie angegeben, so wird nur diese Anzahl Byte geschrieben.
fputs($handle, $var [, <laenge>]);
fclose Schliesst das Handle zu einer geöffneten Datei. So werden die Systemressourcen geschont und anderen Prozessen der Zugriff ermöglicht.
fclose($handle);

Die Funktion "readfile" liest eine Datei und sendet den gesamten Inhalt ohne weitere Bearbeitung an die Standardausgabe - das ist normalerweise der Webbrowser. Am angegebenen Ort (hier "data/news.txt") muss sich natürlich tatsächlich eine Datei befinden, damit sie ausgelesen werden kann.

<b>Newsdatei lesen</b><br />
Und hier sehen Sie unsere aktuellen Neuigkeiten:
<hr noshade size="1">
<?php
$file = "data/news.txt";
echo "Ausgabe der Datei $file:<br />";
readfile($file);
?>

Newsdatei lesen
Und hier sehen Sie unsere aktuellen Neuigkeiten:


Ausgabe der Datei data/news.txt:
Allerlei tolle Neuigkeiten! Noch mehr News bis morgen früh...

Die Funktion "file" liest eine Datei und legt den Inhalt in einem Array ab. Jede Zeile wird zu einem Element eines eindimensionalen Arrays. Der Zeilenumbruch bleibt am Ende des Elements. Das folgende Beispiel liest eine Datei ein und gibt sie zusammen mit Zeilennummern wieder aus.

<?php
$file = "data/news.txt";
echo "Ausgabe der Datei $file:<br />\n";
$filearray = file($file);
foreach($filearray as $num => $line) 
{
  printf ("%02d : %s<br />", $num, $line);
}
?>

Ausgabe der Datei data/news.txt:
00 : Allerlei tolle Neuigkeiten!
01 :
02 : Noch mehr News bis morgen früh...

Das folgende Beispiel liest eine Datei zeilenweise und gibt nur die Zeile aus, die zu einem bestimmten Tag passt. Die auszulesende Textdatei hat dabei folgenden Inhalt.

[Mon]
Heute beginnt die Woche!
[Tue]
Nur noch 4 Tage
[Wed]
Das Wochenende naht
[Thu]
Fast fertig
[Fri]
Freitag nach 1...
[Sat]
Was suchst Du hier heute?
[Sun]
Was suchst Du hier heute?
<b>Newsdatei lesen</b><br />
Und hier sehen Sie Neuigkeiten des Tages:
<hr noshade size="1">
<?php
$select = date("D", time());
$fp = fopen("data/news.txt", "r");
       ## Liest die nächste Zeile aus der angegebenen Datei. Der ganze
       ## Ausdruck wird "FALSE" wenn der Zugriff misslingt. Das ist 
       ## normalerweise nur am Dateiende der Fall. Hier werden also 
       ## Zugriff, Auslesen der Daten und Dateiendeprüfung in einem 
       ## einzigen Ausdruck zusammengefasst.
while ($line = fgets($fp, 1000)) 
{
  ## Ein regulärer Ausdruck zur Prüfung, der den Wochentagsnamen und die
  ## eckigen Klammern am Zeilenanfang erkennt.
  if (preg_match("/^\[[".$select."]+\]/", $line)) 
  {
    ## Der Dateizeiger wird automatisch weitergesetzt, sodass die Ausgabe
    ## mit die nächste Zeile ausgibt.
    echo fgets($fp, 1000)."<br />";
  }
}
fclose($fp);
?>

Newsdatei lesen
Und hier sehen Sie Neuigkeiten des Tages:


Was suchst Du hier heute?

Das folgende Beispiel schreibt das aktuelle Datum und die Uhrzeit sowie die IP-Adresse des Clients in eine Datei und gibt die Datei anschliessend aus. Wenn im Webbrowser wiederholt auf die Schaltfläche "Aktuelle Seite neu laden" bzw. "Aktualisieren" geklickt wird, kann man die Datei wachsen sehen. Das Beispiel funktioniert allerdings nur, wenn der Webbenutzer Schreibrechte im Unterverzeichnis "logdata" hat.

<b>Protokolldatei schreiben</b>
<?php
$logfile = "logdata/logfile.log";
$fp = fopen($logfile, "a");
$logline = sprintf("%s, %s\n", date("d.M.Y h:m:s"), 
                   $_SERVER['REMOTE_ADDR']);
fputs($fp, $logline);
fclose($fp);
echo "<p>Das ist der Inhalt der Protokolldatei:</p>\n";
echo "<pre>";
readfile($logfile);
echo "</pre>";
?>

Protokolldatei schreiben

Das ist der Inhalt der Protokolldatei:

14.Sep.2008 07:09:31, 127.0.0.1

Die Anzeige der Zeilenanzahl kann durch Verbindung der Funktionen "file" und "count" erreicht werden.

<b>Protokolldatei schreiben, Anwendung von file</b>
<?php
echo "<p>Die Protokolldatei hat ";
echo count(file("logdata/logfile.log"));
echo " Zeilen.</p>\n";
?>

Protokolldatei schreiben, Anwendung von file

Die Protokolldatei hat 4 Zeilen.

Manchmal wird der Inhalt einer Datei nicht in einem Array, sondern in einer einzigen langen Zeichenkette benötigt. Vor allem mit Hilfe regulärer Ausdrücke bieten sich gute Verarbeitungsmöglichkeiten an. Einen direkten Befehl gibt es nicht, jedoch eine einfache Befehlskombination. Zur Anwendung kommen die Funktionen "file" (holt die Datei in ein Array) und "implode" (verknüpft Arrayelemente zu einer Zeichenkette). Als Verknüpfungsparameter wird bei "implode" eine leere Zeichenkette ("") verwendet.

$superstring = implode("",(@file("datei.txt")));

Der Zugriff auf Dateien erfolgt immer sequentiell. Es können also niemals am Anfang oder mittendrin Zeilen eingefügt werden. "Anhängen" ist wörtlich zu nehmen - Daten können nur am Ende angefügt werden.

Ebenso probelmatisch ist das gezielte Löschen von Zeilen. Nur mit der Kombination mehrerer Befehle und entsprechendem Leistungsbedarf des Skript ist dies möglich. Das folgende Skript zeigt die grundsätzliche Vorgehensweise.

<?php
$datei = "data/muster.txt";           ## Name der Datei
echo "Originaldatei \"$datei\":<br />\n<pre>";
## Vollständiges Einlesen und Ausgeben der Datei
readfile($datei);
## Zu löschende Zeile
$line = 2;
## Datei wird in ein Array eingelesn
$myfile = file($datei);
## Löschen des betreffenden Elements "$myfile[$line]" aus dem Array
unset($myfile[$line]);
## Bearbeitetes Array wird wieder in die Datei geschrieben
$fh = fopen("{$datei}.bak", "w");     ## Schreibend öffnen 
fputs($fh, implode("", $myfile));     ## Array in String
fclose($fh);
echo "</pre>\n<hr>\nBearbeitete Datei \"$datei.bak\":<br />\n<pre>\n";
readfile("{$datei}.bak");
echo "</pre>\n";
?>

Originaldatei "data/muster.txt":

Zeile 1
Zeile 2
Zeile 3
Zeile 4

Bearbeitete Datei "data/muster.txt.bak":

Zeile 1
Zeile 2
Zeile 4

Grosse Datenmengen sind auf diese Weise allerdings nur schwer handhabbar - in diesem Fall sollten besser Datenbanken verwendet werden, die auf diese Art der Manipulation spezialisiert sind. Wird nur lesend gearbeitet, so können auch die internen Dateizeiger genutzt werden. Beim Schreiben finden diese keine Berücksichtigung.

Dateizeiger-Funktionen

Für Operationen mit Dateizeigern werden folgende Funktionen verwendet.

Funktion Beschreibung
feof Testet, ob der Dateizeiger am Dateiende (engl. end of file) steht. Die Funktion gibt "TRUE" zurück, wenn das Dateiende erreicht wurde.
if (feof($handle))
{
  break;
}
fgetc Holt ein einzelnes Zeichen von der Position des Dateizeigers.
fgets Liest eine bestimmte Zeichenanzahl oder bis zum Zeilenende einer Textdatei. Als Zeilenende wird jeder Zeilenumbruch "\n" erkannt.
fgets($handle, <zeichenanzahl>);
frewind Setzt den Dateizeiger an den Dateianfang.
fseek Setzt den Dateizeiger auf eine bestimmte Position in einer Datei.
ftell Gibt die aktuelle Position des Dateizeigers zurück.

Bei den zuvor gezeigten Beispielen wurde ausgenutzt, dass der Dateizeiger beim Lesen automatisch weiterwandert und beim Schreiben an das Ende der geschriebenen Zeichen gesetzt wird. Möchte man sich aber in einer Textdatei frei bewegen, so hilft nur eine gezielte Positionierung des Dateizeigers. Bei solchen Bewegungen ist es wichtig, nicht versehentlich über das Dateiende hinaus zu lesen. In diesem Fall bricht entweder die genutzte Funktion ab oder PHP reagiert mit einem Laufzeitfehler.

Die Funktion "ftell" gibt die aktuelle Position des Dateizeigers an. Damit kann eine bestimmte Position gemerkt, andere Operationen ausgeführt und dann mit "fseek" die alte Position wiedergefunden werden.

<?php
$file   = "data/muster.txt";
$handle = fopen($file, "r");
rewind($handle);
fgets($handle, 20);
echo "Position des Zeigers: " . ftell($handle) . "<br />\n";
echo "Ausgabe: <tt>";
$pointer = ftell($handle);
for ($i = 0; $i <= 25; $i++) {
  ## Ausgabe in gesperrter Schrift
  echo fgetc($handle) . "&nbsp;";
}
echo "</tt><br />\n";
echo "Position des Zeigers: " . ftell($handle) . "<br />\n";
fseek($handle, $pointer);
echo "Position des Zeigers: " . ftell($handle) . "<br />\n";
?>

Position des Zeigers: 8
Ausgabe: Z e i l e   2   Z e i l e   3 

 Z e i l e   4     
Position des Zeigers: 32
Position des Zeigers: 8

Dateieigenschaften ermitteln

Funktion Beschreibung
clearstatcache Löscht den Status-Cache. Viele Funktionen ermitteln Daten über Dateien und legen diese in einem Speicher in PHP ab, damit spätere Zugriffe schneller ablaufen. Wird eine Datei mehrfach angefragt, so wird sich die Antwort nicht ändern, auch wenn sich die Dateieigenschaften geändert haben. Um die Daten zu aktualisieren muss dieser Status-Cache gelöscht werden.
filemtime Gibt das Datum zurück, an dem die Datei letztmals geändert wurde. Der Rückgabewert ist der Unix-Zeitstempel, muss also (z. B. mit "date") für die Ausgabe formatiert werden.
fileowner Ermittelt den Namen des Besitzers der Datei.
fileperms Holt die Dateiattribute. Jedes Attribut belegt ein Bit im 2 Byte breiten Bitfeld. Es gelten folgende Werte:
Wert Beschreibung
1 Die Datei ist ausführbar (Test mit "is_executable").
2 Die Datei ist schreibbar (Test mit "is_writeable").
4 Die Datei ist lesbar (Test mit "is_readable").
filesize Gibt die Dateigrösse in Byte zurück.
filetype Gibt den Dateityp als Zeichenkette zurück. Zulässige Werte sind: "block", "char", "dir", "fifo", "file" und "link".
is_dir Ermittelt, ob der Name ein Verzeichnis ist. Im Erfolgsfall wird "TRUE" zurückgegeben.
is_executable Ermittelt, ob die Datei ausführbar ist. Im Erfolgsfall wird "TRUE" zurückgegeben.
is_file Ermittelt, ob es sich um eine reguläre Datei handelt. Im Erfolgsfall wird "TRUE" zurückgegeben.
is_link Ermittelt, ob es sich um einen Link (Verweis) handelt. Im Erfolgsfall wird "TRUE" zurückgegeben.
is_readable Ermittelt, ob die Datei lesbar ist. Im Erfolgsfall wird "TRUE" zurückgegeben.
is_writeable Ermittelt, ob in die Datei geschrieben werden kann. Im Erfolgsfall wird "TRUE" zurückgegeben.
stat Gibt in einem Array die folgenden Informationen über eine Datei zurück.
Index Schlüsselwert
1. device Gibt die Nummer des Massenspeichergeräts an.
2. inode Nummer des Inode.
3. inode protection mode Zugriffsrechte auf Inode.
4. number of links Anzahl der mit der Datei bestehenden Verknüpfungen.
5. user id of owner ID des Besitzers (UID).
6. group id of owner UD der Gruppe des Besitzers (GID).
7. device type of inode Gerätetyp des Inode.
8. size in byte Dateigrösse in Byte.
9. time of last access Datum des letzten Zugriffs auf die Datei.
10. time of last modification Datum der letzten Änderung der Datei.
11. time of last change Datum der letzten Dateibewegung.
12. blocksize I/O Blockgrösse des Massenspeichergeräts.
13. numbers of blocks allocated Anzahl der von der Datei teilweise oder ganz belegten Blöcke.

Um die Funktion "fileperms" anzuwenden, wird der Wert mit einem logischen "und" maskiert. Als Maskenwert wird eine Kombination aus den drei Einzelwerten angegeben. Sind alle Rechte erforderlich, entspricht das 7 (1 + 2 + 4). Zur Anzeige jedes Rechtes einzeln kann folgende Lösung interessant sein.

echo (fileperms($entry) & 4) ? "R" : "-";
echo (fileperms($entry) & 2) ? "W" : "-";
echo (fileperms($entry) & 1) ? "X" : "-";

Die Funktion "fileinfo" gibt die Grösse der Datei in Byte zurück. Mit der folgenden Funktion werden Umrechnungen in KByte oder MByte vorgenommen.

<?php
function GetRealVolume($v = 0) 
{
  if ($v > pow(2,10)) 
  {
    if ($v > pow(2,20)) 
    {
      $r  = (integer)($v / pow(2,20));
      $r .= " MB";
    }
    else 
    {
      $r  = (integer)($v / pow(2,10));
      $r .= " KB";
    }
  }
  else 
  {
    $r = (string) $v . " Byte";
  }
  return $r;
}
$datei = "data/muster.txt";
echo GetRealVolume(filesize($datei));
?>
32 Byte

Dateilisten filtern

Eine einfache Dateiliste lässt sich mit der Funktion "glob" erstellen. Das folgende Beispiel zeigt alle Skripte an, deren Dateinamen mit den Buchstaben "a" oder "b" beginnen.

<?php
$path  = basename($_SERVER['PHP_SELF']);
$files = glob("{[ab]*.php}",  GLOB_BRACE);
if (is_array($files))
{
  foreach ($files as $filename)
  {
    echo "$filename<br>";
  }
}
?>
array_asort.php
array_keys.php
array_merge.php
array_multi.php
...

Die Funktion "glob" kann mit den folgenden Schaltern gesteuert werden.

Schalter Beschreibung
GLOB_BRACE Die Platzhalter verwenden Aufzählungssymbolik: {*.txt, *.php} usw.
GLOB_MARK Fügt einen Schrägstrich an alle erkannten Einträge an.
GLOB_NOCHECK Gibt das Suchmuster zurück, wenn keine Dateien gefunden werden.
GLOB_NOESCAPE Metazeichen (Verzeichnistrennzeichen) werden nicht mit einem Backslash markiert.
GLOB_NOSORT Verhindert die Sortierung. (Standard ist eine alphabetische Sortierung)
GLOB_ONLYDIR Nur Verzeichnisse werden erkannt.

Mehrere Schalter können über eine einfache oder-Verknüpfung ("|") kombiniert werden.

GLOB_BRACE | GLOB_ONLYDIR

Die Platzhalterzeichen erlauben folgende Angaben.

Schalter Beschreibung
{Platzhalter,Platzhalter} Eine Serie von oder-verknüpften, durch Kommas getrennte Platzhalterzeichen innerhalb geschweifter Klammern. Das funktioniert, wenn der Schalter "GLOB_BRACE" verwendet wird.
* Soll keinem oder einer beliebigen Anzahl Zeichen entsprechen.
? Ersetzt genau ein beliebiges Zeichen.
[] Genau ein Zeichen aus einer Zeichengruppe, die durch die Angabe in Klammer bestimmt wird. Das kann eine Aufzählung von Zeichen wie in den folgenden Beispielen sein.
[aef] Steht für die Buchstaben "a" oder "e" oder "f".
[a-f] Steht für alle Buchstaben von "a" bis "f".
[0-9] Steht für die Zahlen von "0" bis "9".
[!eEfF] Steht für alle Zeichen ausser "e", "E", "f" und "F". Ein solcher Ausdruck muss auch innerhalb geschweiften Klammern stehen und funktioniert nur, wenn der Schalter "GLOB_BRACE" verwendet wird.

Ersetzen in Dateien

Das mehrfache Ersetzen von Werten innerhalb einer Zeichenkette ist kein Problem, dafür stehen mehrere Funktionen zur Auswahl. Schwieriger wird es, wenn der Ersetzungsvorgang in einer Datei durchgeführt werden soll.

Folgende Funktion zum Ersetzen mit regulären Ausdrücken kann überall eingebaut werden, wo eine derartige Leistung erforderlich ist. Typisch ist die Anwendung zum dauerhaften "Verdichten" von HTML-Dateien durch Entfernung von Leerzeichen und Tabulatoren. Dazu muss der passende reguläre Ausdruck als erster Parameter übergeben werden. Die Dateien "replace.txt" und "replace.bak" müssen zu Beginn bereits vorhanden sein.

<?php
## Funktion wird mit drei Parametern aufgerufen
## - "$string1" ist die zu suchende Zeichenkette
## - "$string2" ist die Ersatzzeichenkette
## - "$filename" ist die Datei, in der gesucht werden soll
function massreplace($string1, $string2, $filename) 
{ 
  ## Datei wird normal geöffnet
  $fp          = fopen($filename, "r");
  ## Dateigrösse wird ermittelt
  $size        = filesize($filename);
  ## Gesamte Datei wird in eine Variable eingelesen
  $contents    = fread($fp, $size);
  ## Datei wird geschlossen
  fclose($fp);
  ## Ersetzungsvorgang wird durchgeführt (die Funktion "preg_replace"
  ## kann hier leicht durch eine eigene Konstruktion ersetzt werden)
  $massreplace = preg_replace("/$string1/", $string2, $contents);
  $fp          = fopen($filename, "w");
  ## Veränderte Datei wird zurückgeschrieben
  fputs($fp, $massreplace);
  fclose($fp);
}
?>
<h3>Mehrfaches Ersetzen in einer Datei</h3>
<form method="post" action="<?=$_SERVER['PHP_SELF']?>">
<table>
  <tr>
    <td>Geben Sie ein Suchwort ein:</td>
    <td><input type="text" name="search" value=""></td>
  </tr>
  <tr>
    <td>Geben Sie den Ersatztest ein:</td>
    <td><input type="text" name="replace" value=""></td>
  </tr>
  <tr>
    <td align=right colspan="2"><input type="Submit" value="Ersetzen"></td>
  </tr>
</table>
</form>
  <table border="0" cellspacing="10">
    <tr>
      <th>Alte Version</th>
      <th>Neue Version</th>
    </tr>
    <tr>
      <td valign=top>
<?php
readfile("data/replace.txt");
echo "</td>";
$search  = $_POST['search'];
$replace = $_POST['replace'];
if (isset($search) && isset($replace))
{
  massreplace($search, 
              "<span style=color:red>$replace</span>", 
              "data/replace.txt");
}
echo "<td valign=top>";
readfile("data/replace.txt");
?>
    </td>
  </tr>
</table>
<?php
copy("data/replace.bak", "data/replace.txt");
?>

<html>

Mehrfaches Ersetzen in einer Datei

<form method="post" action="">

Geben Sie ein Suchwort ein: <input type="text" name="" value="">
Geben Sie den Ersatztest ein: <input type="text" name="" value="">
<input type="Submit" value="Ersetzen">

</form>

Alte Version Neue Version

Vor dem Tor
Vom Eise befreit sind Strom und Bäche
...
Nach der Stadt zurück zu sehen!
Aus dem hohlen finstern Tor

Vor dem Tore

Vom Eise befreit sind Strom und Bäche
...
Nach der Stadt zurück zu sehen!
Aus dem hohlen finstern Tore

</html>