Чтение строк файла: эффективные методы PHP

Раздел: Разработка на PHP -> Работа с файлами

Чтение строк из файла: основные подходы

Основной способ: функция file()

Наиболее простой и часто используемый способ получить все строки файла в виде массива — функция file(). Она открывает файл, читает его содержимое и возвращает каждую строку как отдельный элемент массива. По умолчанию символы перевода строки включаются в конец каждого элемента, но это можно отключить вторым параметром FILE_IGNORE_NEW_LINES. Также можно передать флаги для пропуска пустых строк.

$lines = file('example.txt');
// Без символов новой строки:
$lines = file('example.txt', FILE_IGNORE_NEW_LINES);
// Пропустить пустые строки:
$lines = file('example.txt', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);

Цель применения — когда файл небольшой и помещается в оперативную память. Удобно для конфигурационных файлов, списков, простых логов.

Проблемы: При больших файлах (сотни мегабайт) функция file() может привести к переполнению памяти. Также она читает весь файл сразу, что замедляет работу при ожидании полной загрузки. Еще одна частая ошибка — неучёт кодировки: если файл в UTF-8 с BOM, первые строки могут содержать лишние символы. Рекомендуется сначала проверить наличие BOM и при необходимости удалить.

Как прочитать строки больших файлов без загрузки всего содержимого в память?

Для файлов большого объёма используется комбинация функций fopen(), fgets(), feof() и fclose(). Этот подход читает файл строка за строкой, обрабатывая каждую и не загружая весь файл в память.

$handle = fopen('bigfile.log', 'r');
if ($handle) {
    while (($line = fgets($handle)) !== false) {
        // обработка строки
        echo $line;
    }
    fclose($handle);
} else {
    // ошибка открытия
}

Цель: обработка логов, CSV, любых текстовых файлов размером более десятков мегабайт.

Ошибки: если не проверить, что fopen вернул false, последующие вызовы приведут к фатальной ошибке. Также важно закрывать файл после чтения, иначе ресурсы утекают. При работе с сетевыми файловыми системами возможны таймауты.

Как использовать объектный итератор для удобного чтения строк?

В PHP есть встроенный класс SplFileObject, который позволяет читать файл как итератор. Он предоставляет методы для навигации, пропуска строк, получения номера текущей строки и т.д. Это более высокоуровневый способ по сравнению с прямым использованием fgets.

$file = new SplFileObject('data.txt');
while (!$file->eof()) {
    $line = $file->fgets();
    if ($line !== false) {
        // обработка
    }
}
// Или с foreach:
foreach (new SplFileObject('data.txt') as $lineNum => $line) {
    echo "Строка $lineNum: $line";
}

Цель: когда требуется не только чтение, но и метаданные (номер строки), или хочется использовать объектную парадигму. Хорошо подходит для навигации по большим файлам (можно прыгать на определённую строку с помощью seek).

Потенциальные сложности: если файл не существует, конструктор выбросит исключение, которое нужно перехватывать. Также при использовании foreach для очень больших файлов всё равно память не расходуется, так как итерация ленивая. Однако некоторые методы (например, file()) внутри класса SplFileObject не предназначены для прямого вызова всех строк сразу.

Как разбить на строки предварительно загруженное содержимое файла?

Если файл небольшой и уже загружен в переменную (например, из сети), можно использовать file_get_contents() и explode() с символом перевода строки. Этот способ даёт полный контроль над разделением, в том числе возможность обрабатывать разные окончания строк.

$content = file_get_contents('config.ini');
$lines = explode("\n", $content);
foreach ($lines as $line) {
    $line = trim($line);
    // обработка
}

Цель: когда нужно модифицировать содержимое перед разбиением, например, удалить BOM, или требуется работа со строками в разных кодировках. Также удобно при чтении из строки, а не из файла.

Проблемы: explode не обрабатывает корректно смешанные окончания строк (Windows CRLF, Unix LF). Рекомендуется сначала использовать preg_split('/\r\n|\n|\r/', $content). Также этот способ полностью загружает файл в память, поэтому неприменим для больших файлов.

Расширенные примеры чтения строк

Пример 1: Чтение CSV файла с разбором строк

Пример
$handle = fopen('users.csv', 'r');
$headers = fgetcsv($handle); // первая строка - заголовки
$users = [];
while (($row = fgetcsv($handle)) !== false) {
    $users[] = array_combine($headers, $row);
}
fclose($handle);
print_r($users);
Array
(
    [0] => Array
        (
            [name] => Alice
            [email] => alice@example.com
        )
    [1] => Array
        (
            [name] => Bob
            [email] => bob@example.com
        )
)

Пример 2: Генератор для построчного чтения (ленивый итератор)

Пример
function readLines($filename) {
    $handle = fopen($filename, 'r');
    if (!$handle) {
        throw new RuntimeException("Не удалось открыть файл");
    }
    while (($line = fgets($handle)) !== false) {
        yield rtrim($line, "\r\n");
    }
    fclose($handle);
}

foreach (readLines('large.log') as $lineNum => $line) {
    if (strpos($line, 'ERROR') !== false) {
        echo "$lineNum: $line\n";
    }
}
42: ERROR: Database connection failed
87: ERROR: Timeout reached

Пример 3: Чтение специфических строк с помощью SplFileObject и seek

Пример
$file = new SplFileObject('data.txt');
$file->seek(5); // перейти к строке номер 5 (0-индексация)
echo $file->current(); // вывести строку 5
$file->next();
echo $file->current(); // строка 6
$file->seek(0); // вернуться к началу
Строка 5: пятая строка файла
Строка 6: шестая строка файла

Пример 4: Фильтрация строк при чтении с помощью итератора FilterIterator

Пример
class LineFilter extends FilterIterator {
    public function accept(): bool {
        $line = $this->current();
        return strlen($line) > 10; // только строки длиннее 10 символов
    }
}

$iterator = new LineFilter(new SplFileObject('data.txt'));
foreach ($iterator as $line) {
    echo "Фильтр: $line";
}
Фильтр: длинная строка с текстом
Фильтр: ещё одна запись

Пример 5: Чтение файла с BOM и удаление маркера UTF-8

Пример
$content = file_get_contents('utf8bom.txt');
$bom = pack('H*','EFBBBF');
if (strncmp($content, $bom, 3) === 0) {
    $content = substr($content, 3);
}
$lines = explode("\n", $content);
foreach ($lines as $i => $line) {
    echo "Строка $i: " . trim($line) . "\n";
}
Строка 0: Первая строка без BOM
Строка 1: Вторая строка

Чтение строк файла в PHP - comments

En
Php file lines (php)