fscanf

Parses one line from an open file according to a printf-style format string.

Signature
fscanf(resource $stream, string $format, mixed &...$vars): array|int|false|null
  • Added in PHP 4.0.1

Parameters

ParameterTypeDescription
$streamresourceAn open stream handle, typically returned by fopen().
$formatstringThe format to interpret the line with, using the same conversion specifiers as sprintf() (%s, %d, %f, %x, %c and width modifiers).
$varsoptionalmixedOptional 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

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

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.

Example · php
<?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.

Example · php
<?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.

Example · php
<?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

  • Be the first to comment on this lesson.