Изменение файлов на PHP: от простых замен до сложных манипуляций
Обзор методов изменения файлов
В PHP существует несколько способов изменить содержимое файла. Выбор метода зависит от размера файла, необходимой операции и требований к производительности. Наиболее универсальным и простым решением для небольших файлов является чтение всего содержимого в строку, модификация и обратная запись.
Основной способ: чтение-замена-запись
$file = 'example.txt';
$content = file_get_contents($file);
$newContent = str_replace('старый текст', 'новый текст', $content);
file_put_contents($file, $newContent);Этот подход подходит для файлов размером до нескольких десятков мегабайт. Он прост и не требует управления файловыми указателями.
Возможные проблемы:
- Если файл не существует,
file_get_contentsвернётfalse. Необходима проверка. - При конкурентном доступе может возникнуть состояние гонки. Рекомендуется блокировать файл с помощью
flock. - Для больших файлов может не хватить памяти. Тогда следует использовать построчную обработку.
Как изменить одну конкретную строку в файле?
Если требуется заменить, например, третью строку файла, можно использовать функцию file(), которая возвращает массив строк.
$lines = file('config.txt'); // строки с символами перевода строки
$lines[2] = "новое значение\n"; // замена 3-й строки (индекс 2)
file_put_contents('config.txt', implode('', $lines));Примечание:
индексация начинается с 0. Символы новой строки сохраняются, поэтому при записи их не нужно добавлять дополнительно, если они уже есть в элементах массива.Ошибка: использование file() без параметра FILE_IGNORE_NEW_LINES оставляет символы перевода строки. Если требуется удалить их, следует указывать флаг или обрабатывать строки.
Как изменить файл большого размера, не загружая его целиком в память?
Для больших файлов (гигабайты) применяется построчное чтение из исходного файла и запись во временный файл. Затем исходный файл заменяется временным.
$src = fopen('bigfile.txt', 'r');
$tmp = fopen('bigfile.tmp', 'w');
while (($line = fgets($src)) !== false) {
$line = str_replace('старое', 'новое', $line);
fwrite($tmp, $line);
}
fclose($src);
fclose($tmp);
rename('bigfile.tmp', 'bigfile.txt');Этот метод потребляет минимум памяти, так как обрабатывается одна строка за раз. Функция rename обеспечивает атомарную замену на большинстве файловых систем.
Проблемы: необходимо проверять успешность операций fopen и rename. Если во время записи произойдет ошибка, временный файл останется, и данные могут быть потеряны. Рекомендуется использовать flock для исключения одновременной записи.
Как добавить данные в начало или середину файла?
Вставка в начало или произвольную позицию не поддерживается напрямую. Необходимо переписать файл целиком. Например, добавление строки в начало:
$content = file_get_contents('file.txt');
$content = "Новая первая строка\n" . $content;
file_put_contents('file.txt', $content);Для вставки в середину можно разбить содержимое на части.
Для больших файлов такой подход неэффективен. Следует создавать новый файл и копировать данные, вставляя нужный блок.
Как модифицировать INI-файл?
INI-файлы можно читать с помощью parse_ini_file, изменять массив и записывать обратно. Для записи требуется собственная функция или использование file_put_contents с форматированием.
$config = parse_ini_file('config.ini', true);
$config['database']['host'] = 'newhost';
// функция записи INI
function write_ini_file($path, $data) {
$content = '';
foreach ($data as $section => $values) {
$content .= "[$section]\n";
foreach ($values as $key => $val) {
$content .= "$key = \"$val\"\n";
}
}
file_put_contents($path, $content);
}
write_ini_file('config.ini', $config);Ошибки: некорректная обработка кавычек, пробелов, комментариев. Реализация может быть упрощённой; для production рекомендуется использовать специализированные библиотеки.
Как выполнить замену по регулярному выражению?
Функция preg_replace позволяет заменить все вхождения, соответствующие шаблону. Это полезно для массового редактирования.
$content = file_get_contents('page.html');
$content = preg_replace('/(.*?)<\/oldTag>/s', '$1 ', $content);
file_put_contents('page.html', $content); Проблемы: необходимо правильно экранировать шаблон. Неверное регулярное выражение может привести к ошибке или нежелательным заменам.
Расширенные примеры изменения файлов
Пример 1. Замена с подсчётом количества замен
Использование preg_replace_callback для подсчёта и замены.
$file = 'data.txt';
$content = file_get_contents($file);
$count = 0;
$newContent = preg_replace_callback('/pattern/', function($matches) use (&$count) {
$count++;
return 'replacement';
}, $content);
file_put_contents($file, $newContent);
echo "Заменили: $count";Заменили: 5
Пример 2. Сохранение прав доступа после изменения
После записи файла часто теряются его права. Можно сохранить их до изменения.
$file = 'script.php';
$perms = fileperms($file) & 0777;
$content = file_get_contents($file);
$content = str_replace('old', 'new', $content);
file_put_contents($file, $content);
chmod($file, $perms);(права файла сохранены)
Пример 3. Безопасная запись с блокировкой
Для исключения параллельного доступа используется flock.
$file = 'counter.txt';
$data = file_get_contents($file);
$data++;
$fp = fopen($file, 'w');
if (flock($fp, LOCK_EX)) {
ftruncate($fp, 0);
fwrite($fp, $data);
flock($fp, LOCK_UN);
}
fclose($fp);(файл обновлён, блокировка активна)
Пример 4. Изменение определённого поля в CSV
Предположим, CSV-файл с разделителем запятая; требуется изменить второе поле в строках, где первое поле равно 'id=10'.
$rows = file('data.csv');
foreach ($rows as &$row) {
$fields = str_getcsv($row);
if ($fields[0] == 'id=10') {
$fields[1] = 'newvalue';
$row = implode(',', $fields) . "\n";
}
}
file_put_contents('data.csv', implode('', $rows));(CSV обновлён)
Пример 5. Использование SplFileObject для больших файлов
SplFileObject предоставляет объектно-ориентированный доступ с итерацией.
$file = new SplFileObject('big.txt', 'r+');
$file->setFlags(SplFileObject::DROP_NEW_LINE);
$newLines = [];
foreach ($file as $line) {
$newLines[] = str_replace('x', 'y', $line);
}
$file->ftruncate(0);
$file->rewind();
foreach ($newLines as $line) {
$file->fwrite($line . "\n");
}(большой файл обработан)