Я пытаюсь сделать замену строки для всего файла в PHP. Мой файл превышает 100 МБ, поэтому мне нужно идти по строкам и не могу использовать file_get_contents()
. Есть ли хорошее решение для этого?
Если вы не обязаны использовать PHP, я бы настоятельно рекомендовал выполнять такие вещи из командной строки. Это лучший инструмент для работы и гораздо проще в использовании.
В любом случае команда sed
– это то, что вы ищете:
sed s/search/replace oldfilename > newfilename
Если вам нужна нечувствительность к регистру:
sed -is/search/replace oldfilename > newfilename
Если вам нужно, чтобы это динамически выполнялось в PHP, вы можете использовать passthru()
:
$output = passthru("sed s/$search/$replace $oldfilename > $newfilename");
Ну вот:
function replace_file($path, $string, $replace) { set_time_limit(0); if (is_file($path) === true) { $file = fopen($path, 'r'); $temp = tempnam('./', 'tmp'); if (is_resource($file) === true) { while (feof($file) === false) { file_put_contents($temp, str_replace($string, $replace, fgets($file)), FILE_APPEND); } fclose($file); } unlink($path); } return rename($temp, $path); }
Назовите это так:
replace_file('/path/to/fruits.txt', 'apples', 'oranges');
Получите это несколько строк за раз, выгрузите переменную, получите следующие несколько строк.
$fh = fopen("bigfile.txt", "flags"); $num = 0; $length = 300; $filesize = filesize("bigfile.txt"); while($num < $filesize) { $contents = fread($fh, $length); // .. do stuff ... $num = $num+$length; fseek($fh, $num); } fclose($fh);
Вы захотите убедиться, что это правильно (не проверено). См. Библиотеку документации PHP.
Трудная часть будет записываться в файл. Первая идея, которая появляется у меня в голове, – это заменить строку, записать новый контент в другой файл, а затем в конце удалить старый файл и заменить его на новый.
что-то вроде этого?
$infile="file"; $outfile="temp"; $f = fopen($infile,"r"); $o = fopen($outfile,"a"); $pattern="pattern"; $replace="replace"; if($f){ while( !feof($f) ){ $line = fgets($f,4096); if ( strpos($pattern,"$line") !==FALSE ){ $line=str_replace($pattern,$replace,$line); } fwrite($o,$line); } } fclose($f); fclose($o); rename($outfile,$infile);
Если вы не можете использовать непосредственно sed из командной строки, потому что это динамическая задача, и вам нужно вызвать ее из php, трудно получить синтаксис правильно: вы должны убежать по-разному в строки поиска и замены этих символов
' / $ . * [ ] \ ^ &
Следующая функция выполняет поиск и заменяет строку в файле, не интерпретируя искомую строку как регулярное выражение. Поэтому, если вы хотите, вы могли бы найти строку «. *» И заменить ее на «$».
/** * str_replace_with_sed($search, $replace, $file_in, $file_out=null) * * Search for the fixed string `$search` inside the file `$file_in` * and replace it with `$replace`. The replace occurs in-place unless * `$file_out` is defined: in that case the resulting file is written * into `$file_out` * * Return: sed return status (0 means success, any other integer failure) */ function str_replace_with_sed($search, $replace, $file_in, $file_out=null) { $cmd_opts = ''; if (! $file_out) { // replace inline in $file_in $cmd_opts .= ' -i'; } // We will use Basic Regular Expressions (BRE). This means that in the // search pattern we must escape // $.*[\]^ // // The replacement string must have these characters escaped // \ & // // In both cases we must escape the separator character too ( usually / ) // // Since we run the command trough the shell we We must escape the string // too (yai!). We're delimiting the string with single quotes (') and we'll // escape them with '\'' (close string, write a single quote, reopen string) // Replace all the backslashes as first thing. If we do it in the following // batch replace we would end up with bogus results $search_pattern = str_replace('\\', '\\\\', $search); $search_pattern = str_replace(array('$', '.', '*', '[', ']', '^'), array('\\$', '\\.', '\\*', '\\[', '\\]', '\\^'), $search_pattern); $replace_string = str_replace(array('\\', '&'), array('\\\\', '\\&'), $replace); $output_suffix = $file_out ? " > '$file_out' " : ''; $cmd = sprintf("sed ".$cmd_opts." -e 's/%s/%s/g' \"%s\" ".$output_suffix, str_replace('/','\\/', # escape the regexp separator str_replace("'", "'\''", $search_pattern) // sh string escape ), str_replace('/','\\/', # escape the regexp separator str_replace("'", "'\''", $replace_string) // sh string escape ), $file_in ); passthru($cmd, $status); return $status; }
Я бы использовал «sed» более явным образом, так что вы менее зависимы от своей системы.
$output = passthru("sed -e 's/$search/$replace/g' $oldfilename > $newfilename");