fusionar dos archivos con columnas similares

Tengo dos archivos separados por pestañas que necesito alinear juntos. por ejemplo:

File 1:      File 2:
AAA 123      BBB 345
BBB 345      CCC 333
CCC 333      DDD 444

(¡Estos son archivos grandes, potencialmente miles de líneas!)

Lo que me gustaría hacer es que la salida se vea así:

AAA 123
BBB 345  BBB 345
CCC 333  CCC 333
         DDD 444

Preferiblemente me gustaría hacer esto en perl, pero no estoy seguro de cómo. Cualquier ayuda seria grandemente apreciada.

preguntado el 03 de mayo de 12 a las 19:05

¿realmente necesita repetir la etiqueta de la fila cada vez? construir un hash de arrayrefs sería bastante fácil. -

4 Respuestas

Si solo se trata de hacer una estructura de datos, esto puede ser bastante fácil.

#!/usr/bin/env perl

# usage: script.pl file1 file2 ...

use strict;
use warnings;

my %data;
while (<>) {
  chomp;
  my ($key, $value) = split;
  push @{$data{$key}}, $value;
}

use Data::Dumper;
print Dumper \%data;

A continuación, puede generar en cualquier formato que desee. Si realmente se trata de usar los archivos exactamente como son, entonces es un poco más complicado.

contestado el 03 de mayo de 12 a las 21:05

Suponiendo que los archivos están ordenados,

sub get {
   my ($fh) = @_;
   my $line = <$fh>;
   return () if !defined($line);
   return split(' ', $line);
}

my ($key1, $val1) = get($fh1);
my ($key2, $val2) = get($fh2);

while (defined($key1) && defined($key2)) {
   if ($key1 lt $key2) {
       print(join("\t", $key1, $val1), "\n");
       ($key1, $val1) = get($fh1);
   }
   elsif ($key1 gt $key2) {
       print(join("\t", '', '', $key2, $val2), "\n");
       ($key2, $val2) = get($fh2);
   }
   else {
       print(join("\t", $key1, $val1, $key2, $val2), "\n");
       ($key1, $val1) = get($fh1);
       ($key2, $val2) = get($fh2);
   }
}

while (defined($key1)) {
   print(join("\t", $key1, $val1), "\n");
   ($key1, $val1) = get($fh1);
}

while (defined($key2)) {
   print(join("\t", '', '', $key1, $val1), "\n");
   ($key2, $val2) = get($fh2);
}

contestado el 03 de mayo de 12 a las 20:05

Similar a la respuesta de Joel Berger, pero este enfoque le permite realizar un seguimiento de si los archivos contenían o no una clave determinada:

my %data;

while (my $line = <>){
    chomp $line;
    my ($k)          = $line =~ /^(\S+)/;
    $data{$k}{line}  = $line;
    $data{$k}{$ARGV} = 1;
}

use Data::Dumper;
print Dumper(\%data);

Salida:

$VAR1 = {
  'CCC' => {
    'other.dat' => 1,
    'data.dat' => 1,
    'line' => 'CCC 333'
  },
  'BBB' => {
    'other.dat' => 1,
    'data.dat' => 1,
    'line' => 'BBB 345'
  },
  'DDD' => {
    'other.dat' => 1,
    'line' => 'DDD 444'
  },
  'AAA' => {
    'data.dat' => 1,
    'line' => 'AAA 123'
  }
};

contestado el 03 de mayo de 12 a las 21:05

Como mencionó ikegami, se supone que el contenido de los archivos está organizado como se muestra en su ejemplo.

use strict;
use warnings;

open my $file1, '<file1.txt' or die $!;
open my $file2, '<file2.txt' or die $!;

my $file1_line = <$file1>;
print $file1_line;

while ( my $file2_line = <$file2> ) {
    if( defined( $file1_line = <$file1> ) ) {
        chomp $file1_line;
        print $file1_line;
    }

    my $tabs = $file1_line ? "\t" : "\t\t";
    print "$tabs$file2_line";
}

close $file1;
close $file2;

Al revisar su ejemplo, muestra algunos pares clave/valor idénticos en ambos archivos. Dado esto, parece que desea mostrar los pares exclusivos del archivo 1, exclusivos del archivo 2 y mostrar los pares comunes. Si este es el caso (y no está tratando de hacer coincidir los pares de archivos por claves o valores), puede use List::Compare:

use strict;
use warnings;
use List::Compare;

open my $file1, '<file1.txt' or die $!;
my @file1 = <$file1>;
close $file1;

open my $file2, '<file2.txt' or die $!;
my @file2 = <$file2>;
close $file2;

my $lc = List::Compare->new(\@file1, \@file2);

my @file1Only = $lc->get_Lonly; # L(eft array)only
for(@file1Only) { print }

my @bothFiles = $lc->get_intersection;
for(@bothFiles) { chomp; print "$_\t$_\n" }

my @file2Only = $lc->get_Ronly; # R(ight array)only
for(@file2Only) { print "\t\t$_" }

contestado el 04 de mayo de 12 a las 18:05

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.