fgetcsv

Reads one line from an open file pointer and parses it as CSV, returning the fields as an array.

Signature
fgetcsv(resource $stream, ?int $length = null, string $separator = ",", string $enclosure = "\"", string $escape = "\\"): array|false
  • Added in PHP 4.0

Parameters

ParameterTypeDescription
$streamresourceA valid file pointer opened by fopen(), popen() or fsockopen().
$lengthoptional?int= nullMaximum line length in bytes. 0 or null means no limit, which is what you almost always want.
$separatoroptionalstring= ","The field delimiter. Must be a single-byte character.
$enclosureoptionalstring= "\""The field enclosure character. Must be a single-byte character.
$escapeoptionalstring= "\\"The escape character. Must be a single-byte character or an empty string to disable PHP's proprietary escaping. Relying on the default is deprecated as of PHP 8.4.

Return value

array|falseAn indexed array of the fields read, or false on failure — including when the end of the file is reached.

Errors

Throws a ValueError when separator, enclosure or escape is not a single-byte string (escape may also be an empty string). Emits E_DEPRECATED in PHP 8.4 and later when escape is omitted.

Changelog

  • PHP 8.4.0Relying on the default value of escape is now deprecated — pass it explicitly, normally as an empty string.
  • PHP 8.3.0An empty string is returned instead of a string with a single null byte for the last field if it contains only an unterminated enclosure.
  • PHP 8.0.0length is now nullable.
  • PHP 7.4.0The escape parameter now also accepts an empty string to disable the proprietary escape mechanism.

fgetcsv() reads and parses one record at a time, so memory use stays flat regardless of file size. A two-gigabyte export is no harder than a twenty-row one — which is the whole reason to use it instead of file() plus explode().

It handles the parts of CSV that string splitting gets wrong: quoted fields containing the separator, doubled quotes inside quoted fields, and records that span several physical lines because a quoted field contains a newline.

Always pass the escape argument

PHP's fifth parameter is a non-standard backslash escape that no other CSV implementation uses, and it corrupts real data — a Windows path ending in a backslash before a closing quote breaks the parse. RFC 4180 has no escape character at all. Since PHP 8.4, leaving it out raises a deprecation notice. Pass "" explicitly and your parsing becomes standards-compliant at the same time.

What trips people up

A blank line does not end the loop and does not give you an empty array — it gives you [null], one element containing null. Code that does array_combine($header, $row) then fails on a count mismatch. Skip those rows explicitly.

The $length argument is a legacy performance hint. Leave it null; a value shorter than your longest line silently truncates records.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Import a multi-gigabyte supplier catalogue without exhausting memory_limit
  • Read a semicolon-delimited export from European accounting software
  • Validate an uploaded CSV row by row and report the first bad line number
  • Stream a CSV into batched database inserts a thousand rows at a time

Frequently asked questions

Why do I get a deprecation notice from fgetcsv in PHP 8.4?

Since PHP 8.4, relying on the default value of the escape parameter is deprecated. Pass it explicitly — fgetcsv($h, null, ",", '"', "") — where the empty string disables PHP's non-standard backslash escaping and gives you RFC 4180 behaviour.

What is the difference between fgetcsv and str_getcsv?

fgetcsv() reads from an open file handle one record at a time and correctly handles fields containing newlines; str_getcsv() parses a single string you already have in memory. Use fgetcsv() for files, especially large ones.

Why does fgetcsv return an array containing one null?

That is what a blank line in the file produces. Check for it with if ($row === [null]) continue; before processing, otherwise array_combine() and column access will fail.

More examples

A semicolon-delimited file with a header row

Reading the header separately and feeding it to array_combine() turns each row into a named array, so the arithmetic reads as $row["qty"] rather than $r[1]. The output is 51.00 — three widgets at 9.50 plus ten gadgets at 2.25.

Example · php
<?php
file_put_contents("sales.csv", "product;qty;price\nwidget;3;9.50\ngadget;10;2.25\n");

$h = fopen("sales.csv", "r");
$head = fgetcsv($h, null, ";", "\"", "");
$total = 0.0;
while (($r = fgetcsv($h, null, ";", "\"", "")) !== false) {
    $row = array_combine($head, $r);
    $total += (int) $row["qty"] * (float) $row["price"];
}
fclose($h);

echo "total: ", number_format($total, 2), "\n";
unlink("sales.csv");

Skipping blank lines

The empty third line comes back as an array holding a single null, which prints as an empty string if you do not guard against it and breaks array_combine() outright. The strict comparison against [null] is the standard skip. Trailing blank lines at the end of a file hit this too.

Example · php
<?php
file_put_contents("gaps.csv", "a,b\n1,2\n\n3,4\n");

$h = fopen("gaps.csv", "r");
while (($row = fgetcsv($h, null, ",", "\"", "")) !== false) {
    if ($row === [null]) {
        echo "blank line skipped\n";
        continue;
    }
    echo implode("|", $row), "\n";
}
fclose($h);

unlink("gaps.csv");

Quotes, embedded newlines and separators

The first data record spans two physical lines because the quoted field contains a newline, and fgetcsv() correctly returns it as one field with the newline intact. The second shows doubled quotes decoding to a single literal quote. Neither works if you split the file on "\n" yourself.

Example · php
<?php
$csv = "note,author\n\"Line one\nLine two\",ana\n\"He said \"\"hi\"\"\",bo\n";
file_put_contents("notes.csv", $csv);

$h = fopen("notes.csv", "r");
while (($row = fgetcsv($h, null, ",", "\"", "")) !== false) {
    var_dump($row);
}
fclose($h);

unlink("notes.csv");

Discussion

  • Be the first to comment on this lesson.