2010-03-23 21 views
7

Tengo dos archivos de texto que contienen datos en columnas de la variedad position - value, ordenados por position.¿Cómo puedo recorrer dos archivos simultáneamente en Perl?

Aquí se muestra un ejemplo del primer archivo (archivo A):

100 1 
101 1 
102 0 
103 2 
104 1 
... 

Aquí se muestra un ejemplo del segundo archivo (B):

20 0 
21 0 
... 
100 2 
101 1 
192 3 
193 1 
... 

En lugar de leer uno de los dos archivos en una tabla hash, que es prohibitiva debido a restricciones de memoria, lo que me gustaría hacer es recorrer dos archivos simultáneamente, de forma gradual.

Lo que esto significa es que me gustaría transmitir a través de las líneas de A o B y comparar los valores position.

Si las dos posiciones son iguales, entonces realizo un cálculo en los valores asociados con esa posición.

De lo contrario, si las posiciones no son iguales, me desplazo por las líneas del archivo A o archivo B hasta que las posiciones sean iguales (cuando vuelva a realizar mi cálculo) o llegue a EOF de ambos archivos.

¿Hay alguna manera de hacer esto en Perl?

+0

¿Cuántas líneas en cada archivo? ¿Cuál es la restricción de memoria? –

+0

¿Qué has intentado hasta ahora? ¿Hay algo más sutil que simplemente abrir ambos archivos, leer líneas de cada uno, etc.? –

+1

Crear una tabla hash de múltiples GB o leer uno de los dos archivos en una matriz de memoria no es realizable. Estoy buscando transmitir ambos archivos, usando su propiedad ordenada para pasar por cualquiera de los archivos dependiendo de la posición actual. –

Respuesta

6

Parece un problema con el que probablemente tropezaría, por ejemplo, datos de tabla de base de datos con claves y valores. Aquí hay una implementación del pseudocódigo proporcionado por rjp.

#!/usr/bin/perl 

use strict; 
use warnings; 

sub read_file_line { 
    my $fh = shift; 

    if ($fh and my $line = <$fh>) { 
    chomp $line; 
    return [ split(/\t/, $line) ]; 
    } 
    return; 
} 

sub compute { 
    # do something with the 2 values 
} 

open(my $f1, "file1"); 
open(my $f2, "file2"); 

my $pair1 = read_file_line($f1); 
my $pair2 = read_file_line($f2); 

while ($pair1 and $pair2) { 
    if ($pair1->[0] < $pair2->[0]) { 
    $pair1 = read_file_line($f1); 
    } elsif ($pair2->[0] < $pair1->[0]) { 
    $pair2 = read_file_line($f2); 
    } else { 
    compute($pair1->[1], $pair2->[1]); 
    $pair1 = read_file_line($f1); 
    $pair2 = read_file_line($f2); 
    } 
} 

close($f1); 
close($f2); 

Hope this helps!

+1

Se supone que hay un 'uso de autodie' allí también para verificar las aperturas vacías de errores. ;) – pjf

+1

Esto funcionó bien como inicio, ¡gracias! Una complicación es que la prueba 'while ($ pair1 y $ pair2)' hará que el ciclo termine tan pronto como cualquiera de los archivos llegue a EOF. Mi pregunta, tal como está enmarcada, hace que esto no sea un problema, sin embargo, sí tengo que hacer cosas con los otros dos casos que no son de igualdad de parejas. Así que modifiqué 'read_file_line' para devolver la siguiente línea o la línea actual, y conservo un par de booleanos para verificar si la línea de pares ha cambiado. En lugar de probar EOF, pruebo si ambas líneas se han modificado ejecutando 'read_file_line'. Si es así, entonces puedo salir de forma segura del ciclo 'while'. –

2

Para recorrer los archivos, puede usar el módulo Tie::File. Representa un archivo de texto normal como una matriz.

4

Si los archivos están ordenados, páselos en función de cuál tenga la posición más baja.

Pseudocódigo:

read Apos, Aval from A # initial values 
read Bpos, Bval from B 
until eof(A) or eof(B) 
    if Apos == Bpos then 
    compare() 
    read Apos, Aval from A # advance both files to get a new position 
    read Bpos, Bval from B 
    fi 
    if Apos < Bpos then read Apos, Aval from A 
    if Bpos < Apos then read Bpos, Bval from B 
end 

También es posible usar join(1) para aislar las líneas con las posiciones comunes y proceso que en su tiempo libre.

+0

Está duplicando demasiado código en ese pseudo código. :) –

1

Aquí hay una solución rápida. Si los datos en ambos archivos son bastante equivalentes (por ejemplo, el mismo número de líneas), realmente no es necesario almacenar en tablas hash. Pero pensé que sería útil en caso de que los datos estén codificados.

Código:

open(f1, "<data1"); 
open(f2, "<data2"); 
# initialize hashes 
%data1 =(); 
%data2 =(); 
while(($line1 = <f1>) and ($line2 = <f2>)){ 
    chomp($line1); 
    chomp($line2); 
    # split fields 1 and 2 into an array 
    @LINE1 = split(/\t/, $line1); 
    @LINE2 = split(/\t/, $line2); 
    # store data into hashes 
    $data1{$LINE1[0]} = $LINE1[1]; 
    $data2{$LINE2[0]} = $LINE2[1]; 
    # compare column 2 
    if ($data1{$LINE2[0]} == $data2{$LINE1[0]}){ 
      # compute something 
      $new_val = $data1{$LINE2[0]} + $data2{$LINE1[0]}; 
      print $LINE1[0] . "\t" . $new_val . "\n"; 
    } else { 
      print $LINE1[0] . "\t" . $data1{$LINE1[0]} . "\n"; 
    } 
} 

espero que ayude y quiero saber si es útil.

+0

¡Bienvenido a StackOverflow! Al responder preguntas, intenta dar una breve explicación de lo que hiciste, junto con el código. –

+0

Gracias por el comentario. Soy nuevo, así que asumí que los comentarios en el código serían suficientes. – ssanch

Cuestiones relacionadas