2008-11-19 27 views
6

Estoy trabajando en una secuencia de comandos Perl para leer el archivo CSV y hacer algunos cálculos. El archivo CSV tiene solo dos columnas, algo como a continuación.¿Cómo puedo leer las líneas desde el final del archivo en Perl?

One Two 
1.00 44.000 
3.00 55.000 

Ahora este archivo CSV es muy grande, puede ser de 10 MB a 2 GB.

Actualmente estoy tomando un archivo CSV de un tamaño de 700 MB. Intenté abrir este archivo en el bloc de notas, sobresalir, pero parece que ningún software lo va a abrir.

Quiero leer pueden ser las últimas 1000 líneas del archivo CSV y ver los valores. ¿Cómo puedo hacer eso? No puedo abrir el archivo en el bloc de notas ni en ningún otro programa.

Si escribo un script Perl, entonces necesito procesar el archivo completo para ir al final del archivo y luego leer las últimas 1000 líneas.

¿Hay alguna forma mejor de hacerlo? Soy nuevo en Perl y cualquier sugerencia será apreciada.

He buscado en Internet y hay algunos scripts disponibles como File :: Tail, pero no sé si funcionarán en Windows?

Respuesta

11

En * nix, puede usar el comando tail.

tail -1000 yourfile | perl ... 

Eso escribirá solo las últimas 1000 líneas del programa perl.

En Windows, hay paquetes gnuwin32 y unxutils que tienen la utilidad tail.

+1

Gracias por agregar las versiones de Windows de la cola. –

1

Podría usar el módulo Tie :: File, creo. Parece que esto carga las líneas en una matriz, entonces puedes obtener el tamaño de la matriz y procesar arrayS-ze-1000 hasta arraySize-1.

Tie::File

Otra opción sería la de contar el número de líneas en el archivo, a continuación, recorrer el archivo una vez, y empezar a leer en los valores en numberOfLines-1000

$count = `wc -l < $file`; 
die "wc failed: $?" if $?; 
chomp($count); 

que le daría número de líneas (en la mayoría de los sistemas.

0

Si conoce el número de líneas en el archivo, se puede hacer

perl -ne "print if ($. > N);" filename.csv 

donde N es $ num_lines_in_file - $ num_lines_to_print. Puede contar las líneas con

perl -e "while (<>) {} print $.;" filename.csv 
2
perl -n -e "shift @d if (@d >= 1000); push(@d, $_); END { print @d }" < bigfile.csv 

Aunque en realidad, el hecho de que los sistemas UNIX simplemente tail -n 1000 se deben convencer a basta con instalar cygwin o colinux

+0

Gracias ... Usé la cola y funcionó perfectamente .... – anand

4

Sin cola, un Perl sólo ISN solución Eso es irracional.

Una forma es buscar desde el final del archivo, luego leer las líneas desde él. Si no tiene suficientes líneas, busque aún más desde el final y vuelva a intentarlo.

sub last_x_lines { 
    my ($filename, $lineswanted) = @_; 
    my ($line, $filesize, $seekpos, $numread, @lines); 

    open F, $filename or die "Can't read $filename: $!\n"; 

    $filesize = -s $filename; 
    $seekpos = 50 * $lineswanted; 
    $numread = 0; 

    while ($numread < $lineswanted) { 
     @lines =(); 
     $numread = 0; 
     seek(F, $filesize - $seekpos, 0); 
     <F> if $seekpos < $filesize; # Discard probably fragmentary line 
     while (defined($line = <F>)) { 
      push @lines, $line; 
      shift @lines if ++$numread > $lineswanted; 
     } 
     if ($numread < $lineswanted) { 
      # We didn't get enough lines. Double the amount of space to read from next time. 
      if ($seekpos >= $filesize) { 
       die "There aren't even $lineswanted lines in $filename - I got $numread\n"; 
      } 
      $seekpos *= 2; 
      $seekpos = $filesize if $seekpos >= $filesize; 
     } 
    } 
    close F; 
    return @lines; 
} 

P.S. Un título mejor sería algo así como "Leer líneas desde el final de un archivo grande en Perl".

+0

P.P.S. Agregar un comentario para explicar un downvote sería apreciado. Si siento que la respuesta no fue útil/receptiva, la eliminaría. –

+0

De acuerdo, esta es una solución bastante válida e información útil. +1. –

25

El módulo File::ReadBackwards le permite leer un archivo en orden inverso. Esto facilita la obtención de las últimas N líneas, siempre que no dependa del pedido. Si es así y los datos necesarios son lo suficientemente pequeños (lo que debería ser en su caso) podría leer las últimas 1000 líneas en una matriz y luego reverse.

+0

En segundo lugar, la recomendación. Puede preparar sus propias cosas de búsqueda/lectura, pero no tiene sentido cuando ya está hecho para usted en un módulo de CPAN ampliamente utilizado y bien probado. – ysth

-1

Sin depender de la cola, lo que probablemente haría, si usted tiene más de $ FILESIZE [2 GB?] De la memoria a continuación, que acababa de ser perezoso y hago:

my @lines = <>; 
my @lastKlines = @lines[-1000,-1]; 

Aunque las otras respuestas que implica cola o seek() son prácticamente la forma de hacerlo.

+0

Muy bien, usa la cola, como si no supieras. Preguntaste sobre Perl y esto funciona en Perl. Si hay alguna razón por la cual se considere una respuesta inapropiada, agradecería un comentario. – dlamblin

8

Esto es sólo tangencialmente relacionado con su pregunta principal, pero cuando se quiere comprobar si un módulo como File::Tail trabajos sobre su plataforma, comprobar los resultados de CPAN Testers. Los enlaces en la parte superior de la página del módulo en CPAN Search que conducen a

file-tail-header

En cuanto a la matriz, se ve que, efectivamente, este módulo tiene un problema en Windows en todas las versiones de Perl probado:

file-tail-matrix

0

debe utilizar absolutamente archivo :: cola, o mejor aún otro módulo. No es un script, es un módulo (biblioteca de programación). Es probable que funcione en Windows. Como alguien dijo, puede verificar esto en los comprobadores CPAN, o simplemente leyendo la documentación del módulo o simplemente intentándolo.

Usted ha seleccionado el uso de la utilidad de la cola como su respuesta preferida, pero que es probable que sea más de un dolor de cabeza en Windows de archivos :: cola.

5

Yo he escrito rápida búsqueda de archivos hacia atrás usando el siguiente código en Perl puro:

#!/usr/bin/perl 
use warnings; 
use strict; 
my ($file, $num_of_lines) = @ARGV; 

my $count = 0; 
my $filesize = -s $file; # filesize used to control reaching the start of file while reading it backward 
my $offset = -2; # skip two last characters: \n and ^Z in the end of file 

open F, $file or die "Can't read $file: $!\n"; 

while (abs($offset) < $filesize) { 
    my $line = ""; 
    # we need to check the start of the file for seek in mode "2" 
    # as it continues to output data in revers order even when out of file range reached 
    while (abs($offset) < $filesize) { 
     seek F, $offset, 2;  # because of negative $offset & "2" - it will seek backward 
     $offset -= 1;   # move back the counter 
     my $char = getc F; 
     last if $char eq "\n"; # catch the whole line if reached 
     $line = $char . $line; # otherwise we have next character for current line 
    } 

    # got the next line! 
    print $line, "\n"; 

    # exit the loop if we are done 
    $count++; 
    last if $count > $num_of_lines; 
} 

y ejecutar esta secuencia de comandos como:

$ get-x-lines-from-end.pl ./myhugefile.log 200 
+0

He publicado esto porque no me gustó este "$ seekpos * = 2"; enfoque en la solución anterior que también funciona bien – webdevbyjoss

0

Los módulos son el camino a seguir. Sin embargo, a veces puede escribir un fragmento de código que desee ejecutar en una variedad de máquinas que pueden faltar en los módulos de CPAN más oscuros. En ese caso, ¿por qué no simplemente 'cola' y volcar la salida a un archivo temporal desde dentro de Perl?

#!/usr/bin/perl 

`tail --lines=1000 /path/myfile.txt > tempfile.txt` 

A continuación, tiene algo que no depende de un módulo CPAN si se instala uno puede presentar un problema.

+0

Dijo Notepad y Excel, una cola/usr/bin/perl + podría no ser suficiente. –

Cuestiones relacionadas