En Perl, ¿cómo puedo leer un archivo completo en una cadena?


Estoy tratando de abrir un .archivo html como una gran cadena larga. Esto es lo que tengo:

open(FILE, 'index.html') or die "Can't read file 'filename' [$!]\n";  
$document = <FILE>; 
close (FILE);  
print $document;

Que resulta en:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN

Sin embargo, quiero que el resultado se vea como:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">

De esta manera puedo buscar todo el documento más fácilmente.

Author: johnnyPando, 2009-06-05

17 answers

Añadir:

 local $/;

Antes de leer desde el controlador de archivo. Véase ¿Cómo puedo leer un archivo entero a la vez?, o

$ perldoc -q "entire file"

Véase Variables relacionadas con filehandles en perldoc perlvar y perldoc -f local.

Por cierto, si puede poner su script en el servidor, puede tener todos los módulos que desee. Véase ¿Cómo puedo mantener mi propio directorio de módulos / bibliotecas?.

Además, Path::Class:: File te permite sorber y vomitar.

Path:: Tiny da aún más métodos de conveniencia como slurp, slurp_raw, slurp_utf8 así como sus spew contrapartes.

 73
Author: Sinan Ünür,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-02 01:24:52

Lo haría así:

my $file = "index.html";
my $document = do {
    local $/ = undef;
    open my $fh, "<", $file
        or die "could not open $file: $!";
    <$fh>;
};

Tenga en cuenta el uso de la versión de tres argumentos de open. Es mucho más seguro que las viejas versiones de dos (o uno) argumentos. También tenga en cuenta el uso de un léxico filehandle. Los manejadores de archivos léxicos son más agradables que las antiguas variantes de palabras a pelo, por muchas razones. Estamos aprovechando una de ellas aquí: cierran cuando se salen del alcance.

 92
Author: Chas. Owens,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-02 01:26:35

Con Archivo::Slurp:

use File::Slurp;
my $text = read_file('index.html');

Sí, incluso usted puede usar CPAN.

 71
Author: Quentin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-02 07:29:26

Todos los mensajes son ligeramente no idiomáticos. El modismo es:

open my $fh, '<', $filename or die "error opening $filename: $!";
my $data = do { local $/; <$fh> };

En su mayoría, no hay necesidad de establecer {/a undef.

 46
Author: jrockway,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-06-05 03:20:56

De perlfaq5: ¿Cómo puedo leer un archivo entero a la vez?:


Puede usar el módulo File::Slurp para hacerlo en un solo paso.

use File::Slurp;

$all_of_it = read_file($filename); # entire file in scalar
@all_lines = read_file($filename); # one line per element

El enfoque habitual de Perl para procesar todas las líneas en un archivo es hacerlo una línea a la vez:

open (INPUT, $file)     || die "can't open $file: $!";
while (<INPUT>) {
    chomp;
    # do something with $_
    }
close(INPUT)            || die "can't close $file: $!";

Esto es tremendamente más eficiente que leer el archivo completo en la memoria como una matriz de líneas y luego procesarlo un elemento a la vez, lo que a menudo, si no casi siempre, es el enfoque incorrecto. Cada vez que vea a alguien hacer esto:

@lines = <INPUT>;

Usted debe pensar mucho acerca de por qué necesita todo cargado a la vez. Simplemente no es una solución escalable. También puede que le resulte más divertido usar el módulo estándar Tie::File, o los enlaces DB DB_RECNO del módulo DB_File, que le permiten vincular un array a un archivo para que al acceder a un elemento el array realmente acceda a la línea correspondiente en el archivo.

Se puede leer todo el contenido del filehandle en un escalar.

{
local(*INPUT, $/);
open (INPUT, $file)     || die "can't open $file: $!";
$var = <INPUT>;
}

Que desactiva temporalmente su separador de registros, y cerrará automáticamente el archivo al salir del bloque. Si el archivo ya está abierto, simplemente use esto:

$var = do { local $/; <INPUT> };

Para archivos ordinarios también puede usar la función de lectura.

read( INPUT, $var, -s INPUT );

El tercer argumento prueba el tamaño de bytes de los datos en el manejador de archivos de ENTRADA y lee esos muchos bytes en el búfer var var.

 18
Author: brian d foy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-21 04:46:52

Una manera simple es:

while (<FILE>) { $document .= $_ }

Otra forma es cambiar el separador de registros de entrada "$/". Puede hacerlo localmente en un bloque desnudo para evitar cambiar el separador de registros globales.

{
    open(F, "filename");
    local $/ = undef;
    $d = <F>;
}
 7
Author: Peter Mortensen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-02 01:25:41

O bien establecer $/ a undef (ver la respuesta de jrockway) o simplemente concatenar todas las líneas del archivo:

$content = join('', <$fh>);

Se recomienda usar escalares para los manejadores de archivos en cualquier versión de Perl que lo soporte.

 7
Author: kixx,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-12-27 15:06:48

Otra forma posible:

open my $fh, '<', "filename";
read $fh, my $string, -s $fh;
close $fh;
 4
Author: echo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-02 23:59:17

Solo obtienes la primera línea del operador del diamante <FILE> porque la estás evaluando en contexto escalar:

$document = <FILE>; 

En el contexto list/array, el operador diamond devolverá todas las líneas del archivo.

@lines = <FILE>;
print @lines;
 3
Author: Nathan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-08 20:37:20

Lo haría de la manera más simple, para que cualquiera pueda entender lo que sucede, incluso si hay formas más inteligentes:

my $text = "";
while (my $line = <FILE>) {
    $text .= $line;
}
 2
Author: SomethingSomething,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-05-08 20:07:08
open f, "test.txt"
$file = join '', <f>

<f> - devuelve una matriz de líneas de nuestro archivo (si $/ tiene el valor predeterminado "\n") y luego join '' se pegará esta matriz en.

 2
Author: Тима Епанчинцев,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-03 00:00:14

Esto es más una sugerencia sobre cómo NO hacerlo. Acabo de tener un mal momento para encontrar un error en una aplicación de Perl bastante grande. La mayoría de los módulos tenían sus propios archivos de configuración. Para leer los archivos de configuración como un todo, encontré esta sola línea de Perl en algún lugar de Internet:

# Bad! Don't do that!
my $content = do{local(@ARGV,$/)=$filename;<>};

Reasigna el separador de líneas como se explicó anteriormente. Pero también reasigna la STDIN.

Esto tuvo al menos un efecto secundario que me costó horas encontrar: No se cierra el fichero implícito se maneja correctamente (ya que no llama a close en absoluto).

Por ejemplo, haciendo eso:

use strict;
use warnings;

my $filename = 'some-file.txt';

my $content = do{local(@ARGV,$/)=$filename;<>};
my $content2 = do{local(@ARGV,$/)=$filename;<>};
my $content3 = do{local(@ARGV,$/)=$filename;<>};

print "After reading a file 3 times redirecting to STDIN: $.\n";

open (FILE, "<", $filename) or die $!;

print "After opening a file using dedicated file handle: $.\n";

while (<FILE>) {
    print "read line: $.\n";
}

print "before close: $.\n";
close FILE;
print "after close: $.\n";

Resultados en:

After reading a file 3 times redirecting to STDIN: 3
After opening a file using dedicated file handle: 3
read line: 1
read line: 2
(...)
read line: 46
before close: 46
after close: 0

Lo extraño es que el contador de líneas $. se incrementa por cada archivo en uno. No se reinicia, y no contiene el número de líneas. Y no se restablece a cero al abrir otro archivo hasta que se lea al menos una línea. En mi caso, estaba haciendo algo como esto:

while($. < $skipLines) {<FILE>};

Debido a este problema, la condición era falso porque el contador de línea no se restableció correctamente. No se si esto es un error o simplemente un código equivocado... También llamar a close; oder close STDIN; no ayuda.

Reemplacé este código ilegible usando open, concatenación de cadenas y close. Sin embargo, la solución publicada por Brad Gilbert también funciona, ya que utiliza un controlador de archivo explícito en su lugar.

Las tres líneas al principio pueden ser reemplazadas por:

my $content = do{local $/; open(my $f1, '<', $filename) or die $!; my $tmp1 = <$f1>; close $f1 or die $!; $tmp1};
my $content2 = do{local $/; open(my $f2, '<', $filename) or die $!; my $tmp2 = <$f2>; close $f2 or die $!; $tmp2};
my $content3 = do{local $/; open(my $f3, '<', $filename) or die $!; my $tmp3 = <$f3>; close $f3 or die $!; $tmp3};

Que cierra correctamente el controlador de archivo.

 2
Author: jaw,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-03 00:02:57

Use

 $/ = undef;

Antes de $document = <FILE>;. $/es el separador de registros de entrada , que es una nueva línea por defecto. Al redefinirlo a undef, usted está diciendo que no hay separador de campos. Esto se llama modo "slurp".

Otras soluciones como undef $/ y local $/ (pero no my $/) redeclare $/ y por lo tanto producen el mismo efecto.

 1
Author: Geremia,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-03-14 16:29:46

Simplemente podría crear una sub-rutina:

#Get File Contents
sub gfc
{
    open FC, @_[0];
    join '', <FC>;
}
 0
Author: Sheldon Juncker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-12-30 16:44:52

No se si es una buena práctica, pero solía usar esto:

($a=<F>);
 0
Author: zawy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-31 10:30:06

Todas estas son buenas respuestas. PERO si te sientes perezoso, y el archivo no es tan grande, y la seguridad no es un problema (sabes que no tienes un nombre de archivo contaminado), entonces puedes pagar:

$x=`cat /tmp/foo`;    # note backticks, qw"cat ..." also works
 -1
Author: DaleJ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-09-26 18:34:14

Puedes usar cat en Linux:

@file1=\`cat /etc/file.txt\`;
 -2
Author: user1474509,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-02 23:59:02