заменить строку в большом файле с помощью php

Я пытаюсь заменить строку для всего файла в PHP. Мой файл превышает 100 МБ, поэтому я должен идти построчно и не могу использовать file_get_contents(). Есть ли хорошее решение для этого?


person Brian    schedule 29.01.2010    source источник
comment
Обязательно ли использовать PHP? Если у вас есть доступ к командной строке, вы можете использовать команду sed для выполнения той же функции, вероятно, с гораздо меньшей головной болью. Если это нужно автоматизировать, вы можете создать сценарий оболочки, который запускается в cron.   -  person Dominic Barnes    schedule 29.01.2010
comment
Согласен, это что-то для командной строки, а не PHP.   -  person Mike    schedule 29.01.2010


Ответы (6)


Если вам не требуется использовать PHP, я настоятельно рекомендую выполнять подобные действия из командной строки. Это, безусловно, лучший инструмент для работы, и гораздо проще в использовании.

В любом случае вам нужна команда sed (редактор потока):

sed s/search/replace oldfilename > newfilename

Если вам нужна нечувствительность к регистру:

sed s/search/replace/i oldfilename > newfilename

Если вам нужно, чтобы это выполнялось динамически в PHP, вы можете использовать passthru():

$output = passthru("sed s/$search/$replace $oldfilename > $newfilename");
person Dominic Barnes    schedule 29.01.2010
comment
почему passthru(), а не exec()? - person Brian; 29.01.2010
comment
Любой из них будет работать, но с помощью passthru вы можете получить весь вывод. exec возвращает только последнюю строку вывода. - person Dominic Barnes; 29.01.2010
comment
не так просто использовать passthru для вызова sed, подробности см. В моем ответе. - person Riccardo Galli; 26.06.2013
comment
Для нечувствительности к регистру: sed s/search/replace/i old_file > new_file man sed - person Stack Underflow; 07.04.2020
comment
Я знаю, что это слишком старо.. но как я могу попробовать использовать sed для замены на Ø "sed 's/Ø/Ø' {$this->xml->path} > {$this->xml->path}", но я получаю эту ошибку sh: 1: Oslash: not found sed: -e выражение #1, char 2: unterminated `s' command ш: 1: /Ø: не найдено - person Sean Reyes; 01.05.2020

Ну вот:

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');
person Alix Axel    schedule 29.01.2010
comment
Это была своего рода шутка. Не знаю, почему это было бы смешно. Я передам его нашей JDT (команда разработчиков шуток) и свяжусь с вами через 6-8 недель. - person Tyler Carter; 29.01.2010

Если вы не можете использовать 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;
}
person Riccardo Galli    schedule 26.06.2013

Получить несколько строк за раз, сбросить переменную, получить следующие несколько строк.

$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.

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

person Tyler Carter    schedule 29.01.2010
comment
да, верно... стрим бедняка. Вот как это будет работать, если вы сделаете это в командной строке: cat file | sed 's/replace/something/g' › output.file - person mlathe; 29.01.2010
comment
Я бы запустил php-скрипт, который вызвал функцию командной строки, а затем распечатал выходной файл :) - person Dirk; 29.01.2010
comment
У этого метода есть проблема, если строка, которую вы хотите заменить, длиннее одного символа. Вполне возможно, что строка может охватывать несколько фрагментов данных, из-за чего вы пропустите замену. - person bish; 29.01.2010

что-то вроде этого?

$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);
person ghostdog74    schedule 29.01.2010

Я бы использовал «sed» более явно, чтобы вы меньше зависели от своей системы.

$output = passthru("sed -e 's/$search/$replace/g' $oldfilename > $newfilename");
person Jerome    schedule 04.10.2012