Sscanf: примеры (PHP)
sscanf(string string, string format [, mixed &...vars]): mixedОсновы функции sscanf
Функция sscanf() выполняет форматированный разбор строки, считывая данные из неё согласно заданному шаблону. Эта функция часто применяется для извлечения структурированных данных из текста, например, логов, конфигурационных строк или пользовательского ввода, когда известно ожидаемое форматирование.
mixed sscanf(string $str, string $format, mixed &...$vars)
Функция принимает следующие аргументы:
- $str (string) – исходная строка для разбора. Обязательный параметр.
- $format (string) – строка формата, определяющая, как интерпретировать данные в $str. Состоит из спецификаторов преобразования, начинающихся с символа процента (%). Обязательный параметр.
- &...$vars (mixed) – необязательные переменные, передаваемые по ссылке. В них будут сохранены извлечённые значения. Если параметры не указаны, функция возвращает массив.
Наиболее используемые спецификаторы:
- %d – знаковое целое число (decimal).
- %u – беззнаковое целое число.
- %f – число с плавающей точкой (float).
- %s – строка (string). Чтение останавливается на пробельном символе.
- %[^...] – чтение строки, состоящей из символов, не указанных в скобках.
- %c – чтение одного символа, включая пробельные.
Между % и спецификатором можно указывать:
- * – подавление присваивания (значение считывается, но не сохраняется).
- Число – максимальная ширина поля.
Простые примеры использования
$dateString = "2023-12-25";
$result = sscanf($dateString, "%d-%d-%d", $year, $month, $day);
echo "Год: $year, Месяц: $month, День: $day";
print_r($result);Год: 2023, Месяц: 12, День: 25
Array
(
[0] => 2023
[1] => 12
[2] => 25
)$log = "ERROR 404: Not Found";
// Считываем код ошибки, но подавляем сохранение слова "404", затем считываем сообщение.
sscanf($log, "%s %*d: %[^"]", $level, $message);
echo "Уровень: $level\n";
echo "Сообщение: $message";Уровень: ERROR Сообщение: Not Found
$data = "Иванов,Петр,Сергеевич";
sscanf($data, "%[^,],%[^,],%s", $lastName, $firstName, $patronymic);
echo "Фамилия: $lastName, Имя: $firstName, Отчество: $patronymic";Фамилия: Иванов, Имя: Петр, Отчество: Сергеевич
$input = "255 0xFF 0377";
// %d - десятичное, %x - шестнадцатеричное, %o - восьмеричное
sscanf($input, "%d %x %o", $dec, $hex, $oct);
echo "Десятичное: $dec, Шестнадцатеричное: $hex, Восьмеричное: $oct";Десятичное: 255, Шестнадцатеричное: 255, Восьмеричное: 255
Альтернативные функции в PHP
Разбивает строку на массив по заданному разделителю. Проще в использовании для равномерно разделённых данных, но не подходит для сложных или вариативных шаблонов.
$str = "яблоко,груша,вишня";
$array = explode(",", $str);
print_r($array);Array
(
[0] => яблоко
[1] => груша
[2] => вишня
)Обеспечивают максимальную гибкость для сложных шаблонов поиска и извлечения данных. preg_match() мощнее, но синтаксис регулярных выражений сложнее для чтения и написания по сравнению с простыми шаблонами sscanf().
$str = "Температура: +25.5C";
preg_match('/[+-]?\d+(?:\.\d+)?/', $str, $matches);
echo $matches[0];+25.5
Функции для посимвольного или поточного разбора строк. Требуют больше кода для структурированных данных, но дают полный контроль на низком уровне.
sscanf() предпочтительнее, когда формат строки хорошо определён и относительно прост. Для жёстко структурированных данных (как CSV, фиксированные поля) explode() или fgetcsv() могут быть проще. Для очень сложных или нерегулярных шаблонов лучше подходят регулярные выражения.
Аналоги в других языках программирования
Прямого аналога sscanf() в стандартной библиотеке Python нет. Для похожего поведения используют re.match() или re.search() с группами захвата.
import re
log_line = "ERROR 404: Not Found"
match = re.match(r"(\w+) (\d+): (.+)", log_line)
if match:
level, code, message = match.groups()
print(f"Уровень: {level}, Код: {code}, Сообщение: {message}")Уровень: ERROR, Код: 404, Сообщение: Not Found
Sscanf в Javascript
В JavaScript отсутствует встроенная функция, аналогичная sscanf(). Для разбора строк используют String.prototype.match() с регулярными выражениями или комбинацию методов split(), slice().
let data = "2023-12-25";
let regex = /^(\d{4})-(\d{2})-(\d{2})$/;
let match = data.match(regex);
if (match) {
let [, year, month, day] = match;
console.log(`Год: ${year}, Месяц: ${month}, День: ${day}`);
}Год: 2023, Месяц: 12, День: 25
Функция sscanf() в языке C имеет почти идентичный синтаксис и семантику, что и в PHP, так как PHP позаимствовал её оттуда. Основное отличие в строгой типизации и работе с указателями в C.
#include <stdio.h>
int main() {
char str[] = "42 3.14 hello";
int i;
float f;
char s[20];
sscanf(str, "%d %f %s", &i, &f, s);
printf("%d %.2f %s\n", i, f, s);
return 0;
}42 3.14 hello
В контексте СУБД MySQL для разбора строк в даты существует функция STR_TO_DATE(), которая использует спецификаторы формата, схожие с sscanf(), но специализирована под временные типы данных.
SELECT STR_TO_DATE('25,12,2023', '%d,%m,%Y');2023-12-25
Типичные ошибки и проблемы
Если передано меньше переменных, чем спецификаторов в формате, требующих присваивания, возникает предупреждение.
$str = "10 20 30";
// Передаётся только две переменные для трёх спецификаторов.
$result = @sscanf($str, "%d %d %d", $a, $b); // Используем @, чтобы скрыть предупреждение для примера.
echo "a=$a, b=$b\n";
print_r($result);a=10, b=20
Array
(
[0] => 10
[1] => 20
[2] => 30
)Функция всё равно возвращает все считанные значения в массиве.
Когда начало строки соответствует формату, но далее идёт несоответствие, sscanf() обрабатывает только совпавшую часть.
$str = "Число: 123текст";
$count = sscanf($str, "Число: %d", $number);
echo "Считано элементов: $count\n";
echo "Число: $number\n";Считано элементов: 1 Число: 123
Функция доверяет формату. Неправильно построенный формат или неожиданный ввод могут привести к неверному разбору или неполучению данных.
$userInput = "100 apples";
// Ожидается два числа, но второе - строка.
sscanf($userInput, "%d %d", $count, $price);
echo "Count: $count, Price: $price (некорректно)";Count: 100, Price: (некорректно)
Значение $price останется null или предыдущим, если переменная была определена.
Спецификатор %s останавливается на любом пробельном символе. Для чтения строк, содержащих пробелы, нужно использовать другой спецификатор, например, %[^\n].
$str = "Hello World 123";
sscanf($str, "%s %s %d", $word1, $word2, $num);
echo "$word1, $word2, $num"; // World будет в отдельной переменной.Hello, World, 123
Изменения в новых версиях PHP
Функция sscanf() остаётся стабильной на протяжении многих версий PHP. Ключевые изменения касаются в основном обработки ошибок и производительности.
- В PHP 8.0 была изменена сигнатура функции с точки зрения внутреннего описания аргументов, но это не повлияло на пользовательский код.
- В более ранних версиях (до PHP 7.0) различия были минимальны. Важно отметить, что функция всегда возвращала null в случае, если строка не могла быть обработана согласно формату, или массив/количество присвоенных значений в случае успеха. Это поведение сохраняется.
- В PHP 8 все функции работают в более строгом типизированном контексте, но для sscanf() это не привело к заметным изменениям в использовании.
Расширенные и специальные примеры
function parseLogLine($line) {
// Пытаемся разобрать как минимум уровень и код, сообщение может быть составным.
$parsed = sscanf($line, "%s %d: %[^"]");
if ($parsed[0] !== null) {
return [
'level' => $parsed[0],
'code' => $parsed[1] ?? null,
'message' => $parsed[2] ?? ''
];
}
return null;
}
$lines = [
"INFO 100: Запрос обработан",
"ERROR 500: Internal Server Error",
"WARNING: Неизвестный параметр"
];
foreach ($lines as $line) {
print_r(parseLogLine($line));
echo "\n---\n";
}Array
(
[level] => INFO
[code] => 100
[message] => Запрос обработан
)
---
Array
(
[level] => ERROR
[code] => 500
[message] => Internal Server Error
)
---
Array
(
[level] => WARNING
[code] =>
[message] =>
)
---function isValidIpv4($ip) {
// %d.%d.%d.%d - каждый октет должен быть числом.
// %1d ограничивает ширину октета, предотвращая разбор "192.168.001.001" как одного числа 192168001001.
$count = sscanf($ip, "%3d.%3d.%3d.%3d", $a, $b, $c, $d);
return $count === 4 && $a >= 0 && $a <= 255 && $b >= 0 && $b <= 255 && $c >= 0 && $c <= 255 && $d >= 0 && $d <= 255;
}
$testIps = ["192.168.1.1", "256.0.0.1", "not.an.ip"];
foreach ($testIps as $ip) {
echo "$ip: " . (isValidIpv4($ip) ? 'valid' : 'invalid') . "\n";
}192.168.1.1: valid 256.0.0.1: invalid not.an.ip: invalid
$configLine = "memory_limit=256M;upload_max_filesize=20M;max_execution_time=30";
// Используем sscanf в цикле с strtok для имитации парсера.
$settings = [];
$token = strtok($configLine, ";");
while ($token !== false) {
// Разбираем каждую пару ключ=значение.
sscanf($token, "%[^=]=%s", $key, $value);
$settings[$key] = $value;
$token = strtok(";");
}
print_r($settings);Array
(
[memory_limit] => 256M
[upload_max_filesize] => 20M
[max_execution_time] => 30
)// Предположим, данные имеют фиксированные колонки: ID (3 символа), Name (10 символов), Value (5 символов).
$fixedWidthData = "001ProductA 12.50\n002ItemB 99.99";
$lines = explode("\n", $fixedWidthData);
foreach ($lines as $line) {
// Используем ширину поля: %3s, %10s, %5s
sscanf($line, "%3s%10s%5s", $id, $name, $value);
$name = rtrim($name); // Убираем пробелы фиксированной ширины.
echo "ID: $id, Name: '$name', Value: $value\n";
}ID: 001, Name: 'ProductA', Value: 12.50 ID: 002, Name: 'ItemB', Value: 99.99
$coords = "(45.123, -90.456)";
// Подавляем скобки и запятую с помощью %*c или просто считываем числа между ними.
list($lat, $lon) = sscanf($coords, "(%f, %f)");
echo "Широта: $lat, Долгота: $lon";Широта: 45.123, Долгота: -90.456