CSV-Dateien gehören zu den am weitesten verbreiteten Formaten für den Austausch tabellarischer Daten. Ob Produktlisten, Kundendaten oder Logeinträge: Fast jede Anwendung muss irgendwann CSV-Dateien importieren oder verarbeiten. PHP stellt dafür die Funktion fgetcsv() bereit, die eine CSV-Datei Zeile für Zeile einliest und jede Zeile als Array zurückgibt. In diesem Tutorial werden Syntax, Parameter, typische Anwendungsfälle und häufige Stolperfallen ausführlich erklärt. Am Ende steht ein solides Verständnis dafür, wie CSV-Dateien in PHP zuverlässig und effizient verarbeitet werden.

Zunächst schauen wir uns an, wie die Funktion arbeitet und warum sie für große Dateien besonders gut geeignet ist.
Was ist fgetcsv()?
Die Funktion fgetcsv() liest eine einzelne Zeile aus einer geöffneten CSV-Datei und gibt die enthaltenen Felder als indiziertes Array zurück. Sie arbeitet dabei zeilenweise, was sie besonders speichereffizient macht. Selbst sehr große Dateien mit mehreren Hunderttausend Zeilen lassen sich damit verarbeiten, weil immer nur eine Zeile gleichzeitig im Speicher gehalten wird. Im Gegensatz zu str_getcsv(), das einen bereits vorhandenen String parst, arbeitet fgetcsv() direkt mit einem Datei-Handle, das zuvor mit fopen() geöffnet wurde.
Syntax und Parameter
Die vollständige Signatur der Funktion umfasst fünf Parameter, von denen vier optional sind.
<?php
fgetcsv(
resource $stream,
?int $length = null,
string $separator = ",",
string $enclosure = """,
string $escape = "\"
);
Jeder dieser Parameter steuert einen bestimmten Aspekt des Einlesevorgangs. Die folgenden Abschnitte erläutern ihre Bedeutung im Detail.
Datei-Handle
Der erste Parameter ist ein gültiges Datei-Handle, das zuvor mit fopen() erstellt wurde. Es muss auf eine lesbare Datei zeigen. Ohne ein gültiges Handle gibt fgetcsv() den Wert false zurück und erzeugt eine Warnung.
Maximale Zeilenlänge
Der zweite Parameter legt die maximale Anzahl an Bytes fest, die pro Zeile gelesen werden. Wird er auf null oder 0 gesetzt, liest PHP die gesamte Zeile ohne Längenbegrenzung. Für optimale Performance empfiehlt die PHP-Dokumentation, einen Wert anzugeben, der größer ist als die längste Zeile in der Datei.
Delimiter (Trennzeichen)
Der dritte Parameter bestimmt das Trennzeichen zwischen den einzelnen Feldern. Standardmäßig ist dies ein Komma. In vielen europäischen Ländern, insbesondere in Deutschland, wird jedoch häufig ein Semikolon als Trennzeichen verwendet, da das Komma dort als Dezimaltrennzeichen dient.
Enclosure (Feldbegrenzer)
Der vierte Parameter gibt das Zeichen an, mit dem Felder umschlossen werden. Standard ist das doppelte Anführungszeichen. Felder, die das Trennzeichen, Zeilenumbrüche oder das Enclosure-Zeichen selbst enthalten, werden von diesem Zeichen eingerahmt. Dadurch kann fgetcsv() solche Felder korrekt als Ganzes erkennen.
Escape-Zeichen
Der fünfte Parameter definiert das Zeichen, mit dem das Enclosure-Zeichen innerhalb eines umschlossenen Feldes escaped wird. Standard ist der Backslash. Ab PHP 8.4 wird empfohlen, einen leeren String zu übergeben, da der Backslash als Escape-Zeichen nicht dem CSV-Standard (RFC 4180) entspricht.
CSV-Datei öffnen und zeilenweise lesen
Der typische Arbeitsablauf besteht aus drei Schritten: Die Datei mit fopen() öffnen, in einer while-Schleife mit fgetcsv() jede Zeile einlesen und am Ende die Datei mit fclose() schließen.
<?php
$handle = fopen("produkte.csv", "r");
if ($handle === false) {
die("Datei konnte nicht geöffnet werden.");
}
while (($zeile = fgetcsv($handle)) !== false) {
/* Jede Zeile ist ein Array der Felder */
echo "Produkt: " . $zeile[0] . ", Preis: " . $zeile[1] . "\n";
}
fclose($handle);
Die Bedingung !== false ist wichtig, da fgetcsv() am Ende der Datei den Wert false zurückgibt. Eine einfache Prüfung mit != false würde auch leere Zeilen als Dateiende interpretieren und das Einlesen vorzeitig abbrechen.
Das folgende Diagramm zeigt den Ablauf des zeilenweisen Lesens.
flowchart TD
A["fopen() öffnet die CSV-Datei"] --> B["fgetcsv() liest eine Zeile"]
B --> C{"Rückgabe !== false?"}
C -- "Ja" --> D["Zeile als Array verarbeiten"]
D --> B
C -- "Nein" --> E["Dateiende erreicht"]
E --> F["fclose() schließt die Datei"]
Die erste Zeile als Header verwenden
CSV-Dateien enthalten in der ersten Zeile häufig Spaltenüberschriften. Diese lassen sich nutzen, um aus jeder weiteren Zeile ein assoziatives Array zu erzeugen. Das macht den Zugriff auf die Daten deutlich lesbarer.
<?php
$handle = fopen("kunden.csv", "r");
if ($handle === false) {
die("Datei konnte nicht geöffnet werden.");
}
/* Erste Zeile als Header lesen */
$header = fgetcsv($handle);
$kunden = [];
while (($zeile = fgetcsv($handle)) !== false) {
/* array_combine verbindet Header und Datenzeile */
$kunden[] = array_combine($header, $zeile);
}
fclose($handle);
/* Zugriff über Spaltennamen */
foreach ($kunden as $kunde) {
echo $kunde["Vorname"] . " " . $kunde["Nachname"] . "\n";
}
Die Funktion array_combine() erzeugt ein neues Array, bei dem die Werte des Header-Arrays als Schlüssel dienen. Voraussetzung ist, dass Header und Datenzeile die gleiche Anzahl an Elementen enthalten. Andernfalls gibt array_combine() den Wert false zurück. In der Praxis sollte die Anzahl der Felder deshalb vor dem Zusammenführen geprüft werden.
Unterschiedliche Trennzeichen verwenden
Nicht jede CSV-Datei verwendet das Komma als Trennzeichen. Besonders in Deutschland nutzt Excel standardmäßig das Semikolon, da das Komma als Dezimaltrennzeichen reserviert ist. Auch Tabulatoren kommen als Trennzeichen vor.
<?php
/* CSV-Datei mit Semikolon als Trennzeichen */
$handle = fopen("export_deutsch.csv", "r");
while (($zeile = fgetcsv($handle, 0, ";")) !== false) {
echo $zeile[0] . " | " . $zeile[1] . "\n";
}
fclose($handle);
/* CSV-Datei mit Tabulator als Trennzeichen (TSV) */
$handle = fopen("daten.tsv", "r");
while (($zeile = fgetcsv($handle, 0, "t")) !== false) {
echo $zeile[0] . " | " . $zeile[1] . "\n";
}
fclose($handle);
Der dritte Parameter von fgetcsv() bestimmt das Trennzeichen. Der Wert 0 für die maximale Zeilenlänge sorgt dafür, dass PHP die gesamte Zeile ohne Begrenzung einliest. Bei unbekanntem Trennzeichen kann die erste Zeile der Datei mit fgets() gelesen und auf typische Trennzeichen hin untersucht werden.
Probleme mit Zeichenkodierung (UTF-8 und BOM)
Ein häufiges Problem beim Einlesen von CSV-Dateien ist das sogenannte Byte Order Mark (BOM). Viele Programme, darunter Microsoft Excel, fügen am Anfang von UTF-8-Dateien die unsichtbare Zeichenfolge \xEF\xBB\xBF ein. Dieses BOM ist im Editor nicht sichtbar, verfälscht aber den Inhalt des ersten Feldes.
<?php
$handle = fopen("export_excel.csv", "r");
if ($handle === false) {
die("Datei konnte nicht geöffnet werden.");
}
/* BOM am Dateianfang entfernen */
$bom = fread($handle, 3);
if ($bom !== "xEFxBBxBF") {
/* Kein BOM vorhanden, zurück zum Anfang */
rewind($handle);
}
$header = fgetcsv($handle, 0, ";");
while (($zeile = fgetcsv($handle, 0, ";")) !== false) {
if (count($zeile) === count($header)) {
$datensatz = array_combine($header, $zeile);
echo $datensatz["Name"] . "\n";
}
}
fclose($handle);
Der Code liest die ersten drei Bytes der Datei und prüft, ob sie dem UTF-8 BOM entsprechen. Falls nicht, setzt rewind() den Dateizeiger zurück an den Anfang. Zusätzlich enthält das Beispiel eine Prüfung der Feldanzahl vor dem Aufruf von array_combine(), um Fehler bei unvollständigen Zeilen zu vermeiden.
fgetcsv() vs. SplFileObject::fgetcsv()
PHP bietet mit der Klasse SplFileObject eine objektorientierte Alternative zum prozeduralen Ansatz mit fopen() und fgetcsv(). Das Datei-Handle wird dabei automatisch verwaltet, und die Klasse implementiert das Iterator-Interface, wodurch sie direkt in einer foreach-Schleife verwendet werden kann.
<?php
$datei = new SplFileObject("produkte.csv");
$datei->setFlags(
SplFileObject::READ_CSV |
SplFileObject::SKIP_EMPTY |
SplFileObject::READ_AHEAD
);
$datei->setCsvControl(";", """, "");
foreach ($datei as $zeile) {
if ($zeile === null) {
continue;
}
echo $zeile[0] . " | " . $zeile[1] . "\n";
}
Der Vorteil von SplFileObject liegt in der kompakteren Schreibweise: Das manuelle Öffnen und Schließen der Datei entfällt, und Flags wie SKIP_EMPTY filtern leere Zeilen automatisch heraus. Die Methode setCsvControl() übernimmt dabei die Konfiguration von Trennzeichen, Enclosure und Escape-Zeichen. Für einfache Anwendungsfälle ist der prozedurale Ansatz mit fgetcsv() jedoch ebenso gut geeignet. Die objektorientierte Variante empfiehlt sich vor allem dann, wenn die Datei in einer Klasse gekapselt oder an andere Methoden weitergegeben werden soll.
Häufige Fehler und Lösungen
Beim Arbeiten mit fgetcsv() treten bestimmte Fehler immer wieder auf. Die folgende Übersicht hilft dabei, sie schnell zu erkennen und zu beheben.
Falsches Trennzeichen: Wenn alle Felder einer Zeile in einem einzigen Array-Element landen, stimmt das konfigurierte Trennzeichen nicht mit dem tatsächlichen Trennzeichen der Datei überein. Die Datei sollte in einem Texteditor geöffnet werden, um das verwendete Zeichen zu identifizieren.
Leere Zeilen: fgetcsv() gibt für leere Zeilen ein Array mit einem einzigen null-Element zurück. Dieses Verhalten lässt sich mit einer einfachen Prüfung abfangen.
<?php
$handle = fopen("daten.csv", "r");
while (($zeile = fgetcsv($handle)) !== false) {
/* Leere Zeilen überspringen */
if ($zeile === [null]) {
continue;
}
/* Anzahl der Felder prüfen */
if (count($zeile) < 3) {
error_log("Unvollständige Zeile: " . implode(",", $zeile));
continue;
}
echo $zeile[0] . " | " . $zeile[1] . " | " . $zeile[2] . "\n";
}
fclose($handle);
Datei nicht gefunden: Wenn fopen() den Wert false zurückgibt, existiert die Datei nicht oder die Berechtigungen sind falsch gesetzt. Vor dem Öffnen kann file_exists() und is_readable() geprüft werden.
Speicherprobleme bei großen Dateien: Da fgetcsv() nur eine Zeile gleichzeitig einliest, entstehen Speicherprobleme meist dadurch, dass alle Zeilen in einem Array gesammelt werden. Bei sehr großen Dateien sollte jede Zeile direkt verarbeitet werden, anstatt sie zunächst komplett im Speicher abzulegen.
Zeichenkodierung: Dateien in ISO-8859-1 oder Windows-1252 können Umlaute falsch darstellen. Die Funktion mb_detect_encoding() hilft bei der Erkennung, und mb_convert_encoding() konvertiert den Inhalt nach UTF-8.
Fazit
Die Funktion fgetcsv() ist das zentrale Werkzeug in PHP für das zeilenweise Einlesen von CSV-Dateien. Sie arbeitet speichereffizient, unterstützt konfigurierbare Trennzeichen und Feldbegrenzer und lässt sich flexibel an verschiedene CSV-Formate anpassen. In Kombination mit array_combine() entstehen aus den eingelesenen Zeilen lesbare assoziative Arrays. Für den objektorientierten Ansatz bietet SplFileObject eine elegante Alternative. Wer die typischen Stolperfallen wie BOM-Probleme, falsche Trennzeichen und leere Zeilen kennt, kann CSV-Dateien in PHP zuverlässig und robust verarbeiten.