Navigation
 Startseite
 Fachbücher
 Anzeigenmarkt
 Forum
 Webmaster News
 Script Newsletter
 Push Meldungen
 Kontakt
 Script Installation
 Php
 Php Tutorials
 Lernpfade
 Webhoster Vergleich
 Impressum

Community-Bereich
 kostenlos Registrieren
 Anmelden
 Benutzerliste

Script Datenbank
 Script Archiv
 Script Top 20
 Screenshots
 Testberichte

Suche
 

Unsere Php Scripts
 Counter Script
 Umfrage Script
 Bilder Upload Script
 Terminverwaltung
 Simple PHP Forum
 RSS Grabber

Tools und Generatoren
 .htpasswd Generator
 md5 Generator
 base64 Generator
 Markdown to HTML
 Colorpicker
 Unix timestamp Tool
 Unit Test Generator
 TLD Liste
 Webkatalog‑Verzeichnis

Hosterplus.de
Bekommen Sie Speicherplatz (Webspace), Domains und...
https://www.Hosterplus.de
Artfiles.de
Bietet Serviceorientierte Internetdienstleistungen...
https://www.Artfiles.de
 
 
 

PHP ftruncate() Function: Datei auf Länge kürzen und leeren

Sie befinden sich: Home > Php Tutorial > PHP ftruncate() Function: D...

PHP ftruncate() Function: Datei auf Länge kürzen und leeren
Veröffentlicht am:
18.10.2026
Hits / Besucher:
6
Sprache:
  Deutsch
Tutorial Art:
eigenes
Eingetragen von:
Merkliste:
 
Beschreibung

Ein Besucherzähler in einer Textdatei steht nach einem Abend mit Lastspitze auf 1, obwohl Hunderte Aufrufe kamen. Ein Prozess hatte die Datei im Modus w geöffnet und damit geleert, und genau in diesem Moment las ein zweiter: eine leere Datei, also null, plus eins. PHP ftruncate() verlegt dieses Leeren an die richtige Stelle. Wie man eine Datei öffnet, steht im Artikel zu PHP fopen(), wie man sie gegen parallele Zugriffe sperrt, im Tutorial zu PHP flock(). Hier geht es um den einen Schritt dazwischen: das Kürzen einer Datei, die man bereits in der Hand hält.

Illustration zum Tutorial: PHP ftruncate() Function: Datei auf Länge kürzen und leeren

Wo die Datei endet und wo der Dateizeiger steht, sind zwei getrennte Angaben, und aus genau diesem Umstand entstehen die meisten Fehler.

Was PHP ftruncate() mit einer Datei macht

Die Funktion setzt die Länge einer geöffneten Datei auf eine feste Zahl von Bytes. Ist die neue Länge kleiner, fällt alles dahinter weg. Bei 0 ist die Datei leer, existiert aber weiter, mit denselben Rechten unter demselben Pfad. Das unterscheidet sie von unlink(), das die Datei löscht. Ist die neue Länge größer, wird die Datei verlängert und mit Nullbytes aufgefüllt.

Der Name stammt aus POSIX. Dort gibt es truncate() für einen Pfad und ftruncate() für einen Dateideskriptor, in C mit der Signatur int ftruncate(int fildes, off_t length). PHP bietet nur die zweite Variante an: PHP ftruncate() arbeitet auf einem offenen Handle, nie auf einem Dateinamen.

Die Signatur im Original

Zwei Parameter, ein Wahrheitswert als Ergebnis:

ftruncate(resource $stream, int $size): bool


/* stream: file pointer, must be open
for writing
size: new length in bytes; larger
values pad the file with
null bytes
Returns true on success, false on
failure. */

Was im Block nicht steht: Das Handle muss beschreibbar sein, sonst scheitert der Aufruf. Und PHP ftruncate() verändert nur die Länge der Datei, nicht die Position des Dateizeigers. Schon das kleinste Beispiel prüft deshalb die Rückgabe:

<?php

$fp = fopen('cache.txt', 'r+');
if ($fp === false) {
exit('Datei nicht zu öffnen');
}
if (!ftruncate($fp, 0)) {
exit('Kürzen fehlgeschlagen');
}
fclose($fp);

Der Dateizeiger bleibt stehen

Nach dem Aufruf von PHP ftruncate() zeigt der Dateizeiger genau dorthin, wo er vorher stand. Wer dann schreibt, schreibt an dieser alten Position, und die Lücke davor wird mit Nullbytes gefüllt. Der Versuch lieferte unter Windows mit PHP 8.4.8 und unter Linux mit PHP 8.5.8 dasselbe Ergebnis:

<?php

file_put_contents('test.txt', 'Hallo Welt');

$fp = fopen('test.txt', 'r+');
fseek($fp, 0, SEEK_END); /* Zeiger bei 10 */

var_dump(ftruncate($fp, 0)); /* bool(true) */
var_dump(ftell($fp)); /* int(10) */

fwrite($fp, 'AB');
fclose($fp);

echo bin2hex(file_get_contents('test.txt'));
/* 000000000000000000004142
zehn Nullbytes, dann "AB" */

Die Datei ist jetzt 12 Bytes lang statt 2. Im Editor wirkt sie je nach Darstellung leer oder zeigt seltsame Zeichen vor dem Text. Die Abhilfe ist ein rewind() vor dem Schreiben. Ob es vor oder nach PHP ftruncate() steht, ist gleichgültig, beide Aufrufe müssen nur vor fwrite() kommen. In Beispielen findet man deshalb beide Reihenfolgen, und beide sind richtig.

Datei leeren mit c+, flock und ftruncate()

Lesen, ändern, zurückschreiben: Für einen Zähler oder eine kleine JSON-Datei ist das der häufigste Anwendungsfall. Sicher wird er erst, wenn alle Schritte mit einem einzigen Handle unter einer exklusiven Sperre laufen. Das Diagramm zeigt den Ablauf, dazu den typischen Fehler als Seitenast und ganz oben das Gegenbild mit w:

flowchart TD
    X[Datei schon leer]
    W[fopen mit w] -->|vor der Sperre| X
    A[fopen mit c+] --> B[flock LOCK_EX]
    B --> C[Inhalt lesen]
    C --> D[ftruncate auf 0]
    D --> E[rewind]
    E --> F[fwrite]
    D -.->|ohne rewind| K[fwrite]
    K --> N[Nullbytes vorne]
    F --> G[fflush]
    G --> H[flock LOCK_UN]
    H --> I[fclose]

Als Code mit einem Zähler, der von 41 auf 42 springt:

<?php

$datei = __DIR__ . '/zaehler.txt';

/* c+ legt an, liest und schreibt,
leert aber nicht */
$fp = fopen($datei, 'c+');
if ($fp === false) {
exit('Zähler nicht zu öffnen');
}

flock($fp, LOCK_EX); /* vor jedem Lesen */
$stand = (int) stream_get_contents($fp);

ftruncate($fp, 0); /* Länge auf 0 */
rewind($fp); /* Zeiger auf 0 */
fwrite($fp, (string) ($stand + 1));

fflush($fp); /* vor dem Entsperren */
flock($fp, LOCK_UN);
fclose($fp);

/* vorher 41, nachher 42 */

Jeder Schritt steht an seinem Platz, weil der nächste davon abhängt. Der Modus c+ legt die Datei bei Bedarf an, erlaubt Lesen und Schreiben und lässt den Inhalt beim Öffnen unangetastet. Die Sperre kommt vor dem Lesen, sonst liest ein Prozess einen Stand, den ein anderer gerade ändert. PHP ftruncate() und rewind() bereiten das Schreiben vor. Zuletzt schreibt fflush() den Puffer von PHP in die Datei, bevor die Sperre fällt. Ohne diesen Aufruf sieht der nächste Prozess unter Umständen noch den alten Stand. Sperrmodi, nicht blockierende Sperren und Netzlaufwerke behandelt das Tutorial zu PHP flock().

Bei einer JSON-Datei ändert sich nur der Teil zwischen Lesen und Schreiben:

<?php

$fp = fopen('einstellungen.json', 'c+');
if ($fp === false) {
exit('Datei nicht zu öffnen');
}
flock($fp, LOCK_EX);

$roh = stream_get_contents($fp);
$daten = $roh === '' ? [] : json_decode(
$roh, true, 512, JSON_THROW_ON_ERROR
);
$daten['besuche'] = ($daten['besuche'] ?? 0) + 1;

ftruncate($fp, 0);
rewind($fp);
fwrite($fp, json_encode($daten, JSON_THROW_ON_ERROR));

fflush($fp);
flock($fp, LOCK_UN);
fclose($fp);

Warum der Modus w hier Daten kostet

Der Modus w leert die Datei schon beim Öffnen. Im Test meldete filesize() direkt nach fopen($datei, 'w') bereits 0, bevor irgendetwas geschrieben wurde. Ein flock() danach kommt zu spät. Zwischen Öffnen und Sperren liegt ein Moment, in dem ein zweiter Prozess eine leere Datei liest, und so entsteht der Zähler aus der Einleitung. PHP ftruncate() verschiebt das Leeren hinter die Sperre. Das ist der ganze Grund, warum es im Muster steht.

<?php

/* so nicht: */
$fp = fopen('zaehler.txt', 'w');

/* hier ist die Datei bereits leer,
ein paralleler Leser findet nichts */
flock($fp, LOCK_EX);

/* die Sperre schützt nichts mehr */

Für reines Ersetzen ohne vorheriges Lesen ist file_put_contents() mit LOCK_EX die einfachere Wahl, es erledigt alles in einem Aufruf. Sobald aber unter derselben Sperre erst gelesen und dann geschrieben werden muss, führt kein Weg am offenen Handle und an PHP ftruncate() vorbei.

Welche fopen-Modi mit ftruncate() zusammenpassen

Die Tabelle beschränkt sich auf das, was für das Kürzen zählt. Die Angaben zu r, c, a und a+ sind unter Windows und Linux nachgemessen.

Modus kürzen lesen leert beim Öffnen schreibt
r nein, false ohne Meldung ja nein gar nicht
r+ ja ja nein am Zeiger
c+ ja ja nein am Zeiger
c ja nein nein am Zeiger
w, w+ ja nur w+ ja am Zeiger
a, a+ ja nur a+ nein immer am Ende

Im Modus r liefert PHP ftruncate() einfach false, ohne Warnung und ohne Eintrag in error_get_last(). Wer die Rückgabe nicht prüft, merkt nichts. Und c ohne Plus kann zwar kürzen, aber nicht lesen: stream_get_contents() lieferte dort einen leeren String samt Notice „Bad file descriptor“. Für Lesen, Ändern, Schreiben taugen deshalb nur c+ und r+, wobei r+ eine vorhandene Datei voraussetzt. Alle übrigen Modi erklärt der Artikel Dateihandling mit PHP.

Auf eine bestimmte Länge kürzen

Der zweite Parameter muss nicht 0 sein. Mit der Länge 4 bleibt von einer Datei mit dem Inhalt 0123456789 nur 0123 übrig. Dabei wird nichts umkopiert, das Dateisystem setzt lediglich die Länge neu. Nützlich ist das bei Exporten, die mitten in einer Zeile abbrechen können. Nach jeder vollständigen Zeile merkt sich das Skript die Position mit ftell(). Geht etwas schief, schneidet PHP ftruncate() die halbe Zeile dahinter ab, und die Datei bleibt lesbar:

<?php

$fp = fopen('export.csv', 'w');
$gut = 0;

try {
foreach ($datensaetze as $satz) {
/* schreibt mit mehreren fwrite() */
schreibe_zeile($fp, $satz);
$gut = ftell($fp);
}
} catch (Throwable $e) {
/* halbe Zeile hinter $gut entfernen */
ftruncate($fp, $gut);
}
fclose($fp);

Datei vergrößern: Nullbytes und dünn besetzte Dateien

Ist die neue Länge größer als die aktuelle, erweitert PHP ftruncate() die Datei und füllt sie mit Nullbytes auf. Der Zeiger bleibt auch hier, wo er war:

<?php

file_put_contents('block.bin', 'abc');

$fp = fopen('block.bin', 'r+');
ftruncate($fp, 8);
var_dump(ftell($fp)); /* int(0) */
fclose($fp);

echo bin2hex(file_get_contents('block.bin'));
/* 6162630000000000 */

Platz auf dem Datenträger reserviert das allerdings nicht. Unter Linux ergab ein Aufruf mit 100 MiB eine Datei von 104857600 Bytes, die laut stat() null Blöcke belegte. Das ist eine dünn besetzte Datei: Das Dateisystem merkt sich nur die Länge und belegt Platz erst, wenn tatsächlich Daten hineingeschrieben werden. Wer eine Datei vorab auf feste Größe bringen will, um später nicht an einer vollen Platte zu scheitern, erreicht das mit PHP ftruncate() nicht. Wie genau sich das verhält, hängt vom Dateisystem ab.

Logfiles leeren, während PHP weiterschreibt

Bei der Logrotation wird eine Protokolldatei kopiert und dann geleert, ohne die Anwendung neu zu starten. logrotate nennt das Verfahren copytruncate. Ob es sauber funktioniert, entscheidet nicht der rotierende Prozess, sondern der Modus des Schreibers. Gemessen im Linux-Container mit zwei Handles auf dieselbe Datei: Ein Schreiber im Modus a schrieb 11 Bytes, ein anderes Handle kürzte die Datei mit PHP ftruncate() auf 0, danach schrieb der Schreiber eine neue Zeile. Ergebnis: 4 Bytes, genau wie gewünscht.

Derselbe Ablauf mit einem Schreiber im Modus c, dessen Zeiger bei 11 stand, ergab 15 Bytes: elf Nullbytes und dahinter die neue Zeile. Im Anhängemodus landet jedes fwrite() am aktuellen Ende, egal wo der Zeiger steht. Ein Schreiber mit eigenem Zeiger schreibt dagegen an der alten Position weiter, und die Datei wächst sofort wieder auf die alte Größe an.

<?php

$log = '/var/log/app/app.log';

copy($log, $log . '.1');

$fp = fopen($log, 'c');
flock($fp, LOCK_EX);
ftruncate($fp, 0);
flock($fp, LOCK_UN);
fclose($fp);

/* sauber nur, wenn die Anwendung
ihr Log im Modus a schreibt */

Zeilen, die zwischen copy() und dem Kürzen geschrieben werden, gehen verloren. Das ist der bekannte Preis von copytruncate, für die meisten Protokolle aber vertretbar.

Rückgabewert: false, ValueError und TypeError bei PHP ftruncate()

Die Funktion gibt true oder false zurück, und das false kommt still. Bei einem Handle, das nur zum Lesen geöffnet ist, erscheint weder Warnung noch Notice. Auch php://stdout lieferte unter Linux false ohne Meldung. Laut wird es nur bei falschen Argumenten: Eine negative Länge wirft einen ValueError, ein bereits geschlossenes Handle einen TypeError. Dessen Wortlaut unterscheidet sich zwischen PHP 8.4 und 8.5, eine Prüfung auf den Meldungstext ist deshalb keine gute Idee.

<?php

try {
if (!ftruncate($fp, $laenge)) {
throw new RuntimeException(
'Datei nicht beschreibbar'
);
}
} catch (ValueError $e) {
/* $laenge war negativ */
error_log($e->getMessage());
}

php://temp als String-Puffer und SplFileObject

Bei Speicherstreams arbeitet PHP ftruncate() wie bei Dateien. Mit php://memory und php://temp lieferte der Aufruf true, und von „abcdef“ blieb nach dem Kürzen auf 2 Bytes „ab“. Ein Puffer lässt sich so für den nächsten Durchlauf wiederverwenden, statt ihn jedes Mal neu zu öffnen:

<?php

$puffer = fopen('php://temp', 'w+');

foreach ($stapel as $teil) {
fwrite($puffer, erzeuge_ausgabe($teil));
rewind($puffer);
versende(stream_get_contents($puffer));

ftruncate($puffer, 0);
rewind($puffer);
}

Die objektorientierte Variante heißt SplFileObject::ftruncate() und verhält sich gleich. Auf „abcdef“ im Modus r+ lieferte sie mit der Länge 3 true und ließ „abc“ stehen:

<?php

$datei = new SplFileObject('notiz.txt', 'r+');
if (!$datei->ftruncate(3)) {
echo 'Kürzen fehlgeschlagen';
}

Unbegründet ist dagegen die Sorge, ein fwrite() unmittelbar vor dem Kürzen gehe verloren. PHP schreibt seinen Puffer vorher: Aus „abcdef“ wurde beim Kürzen auf 2 korrekt „ab“. Auch filesize() zeigte direkt danach die neue Länge, ohne clearstatcache().

Fazit

PHP ftruncate() setzt die Länge einer offenen Datei, kürzer, auf null oder länger. Den Dateizeiger rührt es nicht an. Wer danach schreibt, braucht ein rewind() oder ein gezieltes fseek(), sonst stehen Nullbytes vor dem neuen Inhalt.

Seinen eigentlichen Wert hat PHP ftruncate() im Muster aus c+, exklusiver Sperre, Lesen, Kürzen, Zurücksetzen, Schreiben und fflush(). Dort verlegt es das Leeren hinter die Sperre, was der Modus w nicht kann. Und weil ein Nur-Lese-Handle still mit false scheitert, gehört die Prüfung der Rückgabe in jeden produktiven Aufruf.

 


 

Kommentare (0)

Noch keine Kommentare. Sei der Erste!

Melde dich an, um einen Kommentar zu schreiben.
Bücherregal mit drei Büchern: 'PHP 4 - Grundlagen und Profiwissen' von Hanser Verlag, 'Webdesign in a Nutshell' von O'Reilly Verlag, und 'Webgestaltung' von Galileo Computing.