¿Cuál es la mejor manera de abrir y leer un archivo en Perl?

Tenga en cuenta que no estoy buscando la forma “correcta” de abrir / leer un archivo, o la forma en que debería abrir / leer un archivo cada vez. Solo me interesa averiguar de qué manera la mayoría de la gente usa, y tal vez aprender algunos métodos nuevos al mismo tiempo:) *

Un bloque de código muy común en mis progtwigs Perl es abrir un archivo y leer o escribir en él. He visto muchas maneras de hacerlo, y mi estilo al realizar esta tarea ha cambiado varias veces a lo largo de los años. Me pregunto cuál es el mejor método (si hay una mejor manera) para hacer esto.

Solía ​​abrir un archivo como este:

my $input_file = "/path/to/my/file"; open INPUT_FILE, "<$input_file" || die "Can't open $input_file: $!\n"; 

Pero creo que tiene problemas con la captura de errores.

Agregar un paréntesis parece corregir el error de captura:

 open (INPUT_FILE, "<$input_file") || die "Can't open $input_file: $!\n"; 

Sé que también puedes asignar un identificador de archivo a una variable, así que en lugar de usar “INPUT_FILE” como lo hice anteriormente, podría haber usado $ input_filehandle, ¿es mejor así?

Para leer un archivo, si es pequeño, ¿hay algún problema con el globbing, como este?

 my @array = ; 

o

 my $file_contents = join( "\n",  ); 

o siempre deberías recorrer, así:

 my @array; while () { push(@array, $_); } 

Sé que hay tantas maneras de lograr cosas en Perl, solo me pregunto si hay métodos preferidos / estándar para abrir y leer en un archivo.

No hay estándares universales, pero hay razones para preferir uno u otro. Mi forma preferida es esta:

 open( my $input_fh, "< ", $input_file ) || die "Can't open $input_file: $!"; 

Las razones son:

  • Usted informa errores inmediatamente. (Reemplace "morir" por "advertir" si eso es lo que desea).
  • Ahora su manejador de archivos está contado por referencia, por lo que una vez que no lo esté utilizando, se cerrará automáticamente. Si usa el nombre global INPUT_FILEHANDLE, debe cerrar el archivo manualmente o permanecerá abierto hasta que el progtwig finalice.
  • El indicador de modo de lectura "< " está separado del $ input_file, lo que aumenta la legibilidad.

Lo siguiente es excelente si el archivo es pequeño y usted sabe que quiere todas las líneas:

 my @lines = < $input_fh>; 

Incluso puede hacer esto si necesita procesar todas las líneas como una sola cadena:

 my $text = join('', < $input_fh>); 

Para archivos largos, querrás iterar sobre líneas con while, o usar read.

Si quiere el archivo completo como una sola cadena, no hay necesidad de iterar a través de él.

 use strict; use warnings; use Carp; use English qw( -no_match_vars ); my $data = q{}; { local $RS = undef; # This makes it just read the whole thing, my $fh; croak "Can't open $input_file: $!\n" if not open $fh, '< ', $input_file; $data = <$fh>; croak 'Some Error During Close :/ ' if not close $fh; } 

Lo anterior satisface perlcritic --brutal , que es una buena forma de probar las ‘mejores prácticas’ :). $input_file todavía no está definido aquí, pero el rest es kosher.

Tener que escribir “o morir” en todas partes me vuelve loco. Mi forma preferida de abrir un archivo es la siguiente:

 use autodie; open(my $image_fh, '< ', $filename); 

Si bien es muy poco tipeo, hay muchas cosas importantes que notar que están sucediendo:

  • Estamos usando el autodie pragma, lo que significa que todos los integradores de Perl lanzarán una excepción si algo sale mal. Elimina la necesidad de escribir or die ... en su código, produce mensajes de error amigables y legibles por el ser humano, y tiene scope léxico. Está disponible desde el CPAN.

  • Estamos usando la versión de tres argumentos de open. Significa que incluso si tenemos un nombre de archivo gracioso que contiene caracteres como < , > o | , Perl seguirá haciendo lo correcto. En mi tutorial de Perl Security en OSCON, mostré varias maneras de open 2 argumentos para portarse mal. Las notas para este tutorial están disponibles para su descarga gratuita desde Perl Training Australia .

  • Estamos usando un manejador de archivo escalar. Esto significa que no vamos a cerrar de manera coincidente el identificador de archivo de otra persona con el mismo nombre, lo que puede suceder si usamos identificadores de archivo de paquete. También significa que se pueden detectar errores de ortografía strict , y que nuestro identificador de archivo se limpiará automáticamente si se sale del scope.

  • Estamos usando un manejador de archivo significativo . En este caso, parece que vamos a escribir en una imagen.

  • El controlador de archivo termina con _fh . Si vemos que lo usamos como un escalar normal, entonces sabemos que probablemente sea un error.

Si sus archivos son lo suficientemente pequeños como para poder leer todo en la memoria, use File :: Slurp . Lee y escribe archivos completos con una API muy simple, además de realizar todas las comprobaciones de errores para que no sea necesario.

No hay mejor manera de abrir y leer un archivo. Es la pregunta incorrecta para preguntar. ¿Qué hay en el archivo? ¿Cuántos datos necesitas en cualquier punto? ¿Necesitas todos los datos a la vez? ¿Qué necesitas hacer con los datos? Debe averiguarlos antes de pensar en cómo debe abrir y leer el archivo.

¿Algo que estás haciendo ahora te está causando problemas? Si no, ¿no tienes mejores problemas para resolver? 🙂

La mayor parte de su pregunta es meramente syntax, y eso es todo lo que se responde en la documentación de Perl (especialmente ( perlopentut ). También es posible que desee obtener Learning Perl , que responde a la mayoría de los problemas que tiene en su pregunta.

Buena suerte, 🙂

Es cierto que hay tantas formas mejores de abrir un archivo en Perl como hay

 $files_in_the_known_universe * $perl_programmers 

… pero sigue siendo interesante ver quién lo hace de esa forma. Mi forma preferida de sorber (leer el archivo completo a la vez) es:

 use strict; use warnings; use IO::File; my $file = shift @ARGV or die "what file?"; my $fh = IO::File->new( $file, '< ' ) or die "$file: $!"; my $data = do { local $/; <$fh> }; $fh->close(); # If you didn't just run out of memory, you have: printf "%d characters (possibly bytes)\n", length($data); 

Y al ir línea por línea:

 my $fh = IO::File->new( $file, '< ' ) or die "$file: $!"; while ( my $line = <$fh> ) { print "Better than cat: $line"; } $fh->close(); 

Advertencia: por supuesto, estos son solo los enfoques que he aplicado a la memoria muscular para el trabajo diario, y pueden ser radicalmente inadecuados para el problema que estás tratando de resolver.

Para OO, me gusta:

 use FileHandle; ... my $handle = FileHandle->new( "< $file_to_read" ); croak( "Could not open '$file_to_read'" ) unless $handle; ... my $line1 = <$handle>; my $line2 = $handle->getline; my @lines = $handle->getlines; $handle->close; 

Una vez usé el

 open (FILEIN, "< ", $inputfile) or die "..."; my @FileContents = ; close FILEIN; 

repetitivo regularmente. Hoy en día, uso File::Slurp para archivos pequeños que quiero mantener completamente en la memoria, y Tie::File para archivos grandes que quiero abordar de manera escasa y / o archivos que deseo cambiar en su lugar.

Lea todo el archivo $ file en texto variable $ con una sola línea

 $text = do {local(@ARGV, $/) = $file ; <>}; 

o como una función

 $text = load_file($file); sub load_file {local(@ARGV, $/) = @_; <>} 

Si estos progtwigs son solo para su productividad, ¡lo que funcione! Incorpore todo el manejo de errores que crea que necesita.

Leer en un archivo completo si es grande puede no ser la mejor manera a largo plazo para hacer las cosas, por lo que es posible que desee procesar las líneas tal como vienen en lugar de cargarlas en una matriz.

Un consejo que obtuve de uno de los capítulos de The Pragmatic Programmer (Hunt & Thomas) es que tal vez desee que el script guarde una copia de seguridad del archivo antes de que funcione para cortar y cortar en cubitos.

El || el operador tiene mayor prioridad, por lo que se evalúa primero antes de enviar el resultado a “abrir” … En el código que ha mencionado, use el operador “o” en su lugar, y no tendría ese problema.

 open INPUT_FILE, "< $input_file" or die "Can't open $input_file: $!\n"; 

Damian Conway lo hace de esta manera:

 $data = readline!open(!((*{!$_},$/)=\$_)) for "filename"; 

Pero no te recomiendo eso.