PHP でファイル全体を文字列置換しようとしています。私のファイルは 100MB を超えているので、1 行ずつ移動する必要があり、使用できませんfile_get_contents()
。これに対する良い解決策はありますか?
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");
どうぞ:
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');
動的なタスクであり、php から呼び出す必要があるため、コマンド ラインから直接 sed を使用できない場合、正しい構文を取得するのは困難です。検索文字列と置換文字列でこれらの文字をさまざまな方法でエスケープする必要があります。
' / $ . * [ ] \ ^ &
次の関数は、検索された文字列を正規表現として解釈せずに、ファイル内の文字列を検索して置換します。したがって、文字列「.*」を検索して、「$」に置き換えることもできます。
/**
* 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");
このようなもの?
$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);
一度に数行ずつ取得し、変数をダンプして、次の数行を取得します。
$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 ドキュメントのライブラリを参照してください。
トリッキーな部分は、ファイルに書き戻すことです。私の頭に浮かぶ最初のアイデアは、文字列の置換を行い、新しいコンテンツを別のファイルに書き込み、最後に古いファイルを削除して新しいファイルに置き換えるというものです。