2010-01-29 28 views
8

Estoy tratando de hacer una cadena de reemplazar todo el archivo en PHP. Mi archivo tiene más de 100 MB, así que tengo que ir línea por línea y no puedo usar file_get_contents(). ¿Hay una buena solución para esto?cadena reemplazar en un archivo grande con php

+2

¿Es necesario usar PHP? Si tiene acceso a la línea de comando, puede usar el comando 'sed' para realizar esta misma función, probablemente con un dolor de cabeza mucho menor. Si necesita ser automatizado, puede hacer un script de shell que se ejecute en cron. –

+1

De acuerdo, esto es algo para la línea de comandos, no para PHP. – Mike

Respuesta

10

Si no está obligado a utilizar PHP, recomiendo realizar cosas como esta desde la línea de comandos. Es, con mucho, la mejor herramienta para el trabajo y mucho más fácil de usar.

En cualquier caso, el comando sed es lo que busca:

sed s/search/replace oldfilename > newfilename 

Si usted necesita la caja del insensibilidad:

sed -i s/search/replace oldfilename > newfilename 

Si necesita esto para realizar dinámicamente dentro de PHP, puede usar passthru():

$output = passthru("sed s/$search/$replace $oldfilename > $newfilename"); 
+1

¿por qué 'passthru()' y no 'exec()'? – Brian

+3

Cualquiera de los dos funcionaría, pero usando passthru, puede obtener el resultado completo. exec solo devuelve la última línea de la salida. –

+0

no es tan fácil de usar passthru para llamar a sed, ver mi respuesta para más detalles. –

1

Obténgalo unas pocas líneas a la vez, descargue la variable, obtenga las siguientes líneas.

$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); 

Te querrás asegurar de que sea correcto (no lo has probado). Consulte la biblioteca en PHP Documentation.

La parte engañosa va a volver a escribir en el archivo. La primera idea que me viene a la mente es reemplazar cadenas, escribir el nuevo contenido en otro archivo y luego, al final, eliminar el archivo anterior y reemplazarlo por uno nuevo.

+0

Sí, está bien ... un hombre pobre está transmitiendo.Que efectivamente funcionaría si lo hicieras en la línea de comando: archivo cat | sed 's/replace/something/g'> output.file – mlathe

+0

Me gustaría ejecutar un script php que llamara a la función de línea de comando - luego imprimí el archivo de salida :) – Dirk

+1

Este método tiene un problema si la cadena que estás buscando para reemplazar es más largo que un solo personaje. Es muy posible que la cadena pueda abarcar múltiples fragmentos de datos, lo que hace que omita el reemplazo. – bish

6

Aquí van:

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); 
} 

llamada así:

replace_file('/path/to/fruits.txt', 'apples', 'oranges'); 
+0

Supongo que tienes esta función por ahí. –

+1

@ Chacha102: No, acabo de codificarlo. ¿Por qué? –

+5

Fue una especie de broma. No sé por qué habría sido divertido. Voy a dárselo a nuestro JDT (Equipo de Desarrollo de Bromas) y contactarte de 6 a 8 semanas. –

1

algo como esto?

$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); 
1

Hubiera usado 'sed' de una manera más explícita, por lo que es menos dependiente de su sistema.

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

Si no puede utilizar directamente sed desde la línea de comandos, porque es una tarea dinámica y hay que llamarlo desde php que es difícil de obtener el derecho de sintaxis: debe escapar de diferentes maneras en la búsqueda y reemplazo cuerdas estos personajes

'/$ . * [ ] \^& 

la siguiente función de búsqueda y reemplazar una cadena en un archivo sin interpretar la cadena buscada como una expresión regular. Entonces, si lo desea, podría buscar la cadena ". *" Y reemplazarla por "$".

/** 
* 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; 
} 
Cuestiones relacionadas