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

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

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

lo que resulta en:

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

Sin embargo, quiero que el resultado se vea así:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">

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

Añadir:

  local $/; 

antes de leer desde el manejador de archivo. Consulte ¿Cómo puedo leer en un archivo completo de una vez? , o

  $ perldoc -q "archivo completo" 

Ver variables relacionadas con filehandles en perldoc perlvar y perldoc -f local .

Por cierto, si puedes poner tu script en el servidor, puedes tener todos los módulos que quieras. Ver ¿Cómo guardo mi propio directorio de módulo / biblioteca? .

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

Path :: Tiny ofrece incluso más métodos de conveniencia como slurp , slurp_raw , slurp_utf8 y sus equivalentes de slurp_utf8 .

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 versiones antiguas de dos (o un) argumentos. También tenga en cuenta el uso de un manejador de archivos léxico. Los manejadores de archivos léxicos son más bonitos que las antiguas variantes de palabras simples, por muchas razones. Estamos aprovechando uno de ellos aquí: cierran cuando salen del scope.

Con File :: Slurp :

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

Sí, incluso puedes usar CPAN .

Todas las publicaciones son ligeramente no idiomáticas. El modismo es:

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

En general, no es necesario establecer $ / undef .

De perlfaq5: ¿Cómo puedo leer en un archivo completo de una 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 () { chomp; # do something with $_ } close(INPUT) || die "can't close $file: $!"; 

Esto es tremendamente más eficiente que leer todo el archivo en la memoria como un conjunto de líneas y luego procesarlo un elemento a la vez, que a menudo es, si no casi siempre, el enfoque equivocado. Cuando veas a alguien hacer esto:

 @lines = ; 

deberías pensar mucho sobre por qué necesitas todo cargado a la vez. Simplemente no es una solución escalable. También puede ser más divertido utilizar el módulo Tie :: File estándar o los enlaces $ DB_RECNO del módulo DB_File, que le permiten vincular una matriz a un archivo para que al acceder a un elemento la matriz realmente acceda a la línea correspondiente en el archivo .

Puede leer todo el contenido del manejador de archivos en un escalar.

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

Eso no define temporalmente su separador de registros, y cerrará automáticamente el archivo en la salida del bloque. Si el archivo ya está abierto, solo usa esto:

 $var = do { local $/;  }; 

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 identificador de archivos INPUT y lee tantos bytes en el buffer $ var.

Una forma simple es:

 while () { $document .= $_ } 

Otra forma es cambiar el separador de registro de entrada “$ /”. Puede hacerlo localmente en un bloque desnudo para evitar cambiar el separador de registros global.

 { open(F, "filename"); local $/ = undef; $d = ; } 

undef $/ undef (vea la respuesta de jrockway) o simplemente concatene todas las líneas del archivo:

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

Se recomienda utilizar escalares para manejadores de archivos en cualquier versión de Perl que lo admita.

Otra posible forma:

 open my $fh, '< ', "filename"; read $fh, my $string, -s $fh; close $fh; 

Solo obtiene la primera línea del operador de diamante porque la está evaluando en contexto escalar:

 $document = ; 

En el contexto de lista / matriz, el operador de diamante devolverá todas las líneas del archivo.

 @lines = ; print @lines; 

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 = ) { $text .= $line; } 
 open f, "test.txt" $file = join '',  

– devuelve una matriz de líneas de nuestro archivo (si $/ tiene el valor predeterminado "\n" ) y luego join '' insertará esta matriz.

Esto es más una sugerencia sobre cómo NO hacerlo. Me fue mal 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 línea única de Perl en algún lugar de Internet:

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

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

Esto tuvo al menos un efecto secundario que me costó horas encontrar: no cierra correctamente el identificador de archivo implícito (ya que no close de ninguna manera).

Por ejemplo, hacer 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 () { 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ínea $. se incrementa para cada archivo por uno. No se restablece, y no contiene el número de líneas. Y no se restablece a cero cuando se abre otro archivo hasta que se lee al menos una línea. En mi caso, estaba haciendo algo como esto:

 while($. < $skipLines) {}; 

Debido a este problema, la condición era falsa porque el contador de línea no se restableció correctamente. No sé si esto es un error o simplemente un código incorrecto … También estoy llamando a close; o close STDIN; no ayuda.

Reemplacé este código ilegible usando open, string concatenation y close. Sin embargo, la solución publicada por Brad Gilbert también funciona, ya que utiliza un manejador de archivo explícito.

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 identificador del archivo.

Utilizar

  $/ = undef; 

before $document = ; . $/ es el separador de registros de entrada , que es una nueva línea por defecto. Al redefinirlo a undef , estás diciendo que no hay separador de campo. Esto se llama modo “sorber”.

Otras soluciones como undef $/ y local $/ (pero no my $/ ) redeclaran $ / y producen el mismo efecto.

Simplemente podría crear una sub-rutina:

 #Get File Contents sub gfc { open FC, @_[0]; join '', ; } 

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

 ($a=); 

Estas son todas buenas respuestas. PERO, si te sientes flojo, y el archivo no es tan grande, y la seguridad no es un problema (ya sabes que no tienes un nombre de archivo contaminado), puedes pagar:

 $x=`cat /tmp/foo`; # note backticks, qw"cat ..." also works 

Puedes usar cat en Linux:

 @file1=\`cat /etc/file.txt\`;