fscanf
Parses one line from an open file according to a printf-style format string.
fscanf(resource $stream, string $format, mixed &...$vars): array|int|false|null- Added in PHP 4.0.1
Parameters
| Parameter | Type | Description |
|---|---|---|
$stream | resource | An open stream handle, typically returned by fopen(). |
$format | string | The format to interpret the line with, using the same conversion specifiers as sprintf() (%s, %d, %f, %x, %c and width modifiers). |
$varsoptional | mixed | Optional variables passed by reference that receive the parsed values instead of them being returned as an array. |
Return value
array|int|false|nullAn array of the parsed values when only $stream and $format are given; the number of assigned values when $vars are passed; null when the line yields no values at all; false when the end of the stream has been reached, or on other errors.
Errors
Throws a TypeError if $stream is not an open stream resource; an unknown conversion specifier emits E_WARNING and the call returns false.
fscanf reads exactly one line from a stream and splits it according to a format string, the reverse of printf. Each call advances the pointer to the next line, so it is normally used in a loop.
When to reach for it
It shines on machine-generated, column-aligned text where the shape of every line is identical: /etc/passwd-style records, fixed-width reports, simple log lines, coordinate dumps. It also converts types for you — %d yields an int and %f a float, so you do not have to cast afterwards.
What trips people up
Three things. First, whitespace in the format is not literal: %s %d matches any run of spaces or tabs, and %s itself stops at the first whitespace character, so it can never capture a value with a space in it. Use a width like %10s or a character class alternative such as fgetcsv when fields contain spaces.
Second, the return value has several shapes. With two arguments you get an array; with by-reference variables you get an int count. At end of file fscanf returns false, and a line that yields no values at all returns null. Both are falsy, so while ($row = fscanf($fp, ...)) terminates correctly; if you prefer a strict test, use while (($row = fscanf($fp, ...)) !== false).
Third, a line that does not match the format returns a partially filled array with null entries rather than an error, so validate before you use the values.
Example
When to use it
- Parse a fixed-width report where every line is "CODE QTY PRICE" into typed values without regex.
- Read a colon-separated system file such as an /etc/passwd export line by line.
- Load a simple coordinate or measurement dump where each line holds two or three numbers.
Frequently asked questions
What is the difference between fscanf and sscanf?
sscanf parses a string you already have, while fscanf reads and consumes the next line from an open stream. fscanf is effectively fgets() followed by sscanf() on the result.
How should I loop over every line with fscanf?
Once the stream is exhausted fscanf returns false, so both while ($row = fscanf($fp, $format)) and the strict while (($row = fscanf($fp, $format)) !== false) terminate. Prefer the strict form, because a line that yields no values returns null and a line that parses to a single 0 or empty string still gives you a truthy array.
Can fscanf read CSV files?
Not reliably. It splits on whitespace and has no concept of quoted fields or embedded commas, so use fgetcsv for comma- or semicolon-separated data and keep fscanf for whitespace-aligned columns.
More examples
Assigning straight into variables
With by-reference arguments fscanf returns the number of values it assigned (3 here) instead of an array, and $sku, $qty and $price are already a string, an int and a float. The loop guard uses > 0 because end of input returns null and a failed parse can return -1.
<?php
$file = "stock.txt";
file_put_contents($file, "WIDGET 12 4.50\nGIZMO 3 19.99\n");
$fp = fopen($file, "r");
while (($n = fscanf($fp, "%s %d %f", $sku, $qty, $price)) > 0) {
echo "$n fields: $sku x$qty = ", number_format($qty * $price, 2), "\n";
}
fclose($fp);
unlink($file);Types are converted, not just split
The output shows int(42), float(3.5), int(255) from the hexadecimal %x, and string "hello". This is the main advantage over explode(): you get native types back and do not have to cast or trim anything.
<?php
$fp = fopen("php://memory", "r+");
fwrite($fp, "42 3.5 ff hello\n");
rewind($fp);
$row = fscanf($fp, "%d %f %x %s");
var_dump($row);
fclose($fp);Why %s cannot hold a space
The first call captures only "Ada" and then fails to find a number where "Lovelace" sits, so the integer slot comes back null. The character-class form %[^0-9] grabs everything up to the first digit, which is how you match a field containing spaces.
<?php
$fp = fopen("php://memory", "r+");
fwrite($fp, "Ada Lovelace 1815\n");
rewind($fp);
print_r(fscanf($fp, "%s %d")); // stops at the space
rewind($fp);
print_r(fscanf($fp, "%[^0-9]%d"));
fclose($fp);
Discussion