Quanti modi diversi ci sono per concatenare due file riga per riga usando Perl?
-
06-07-2019 - |
Domanda
Supponiamo che file1 assomigli a questo:
bye bye hello thank you
E file2 è simile al seguente:
chao hola gracias
L'output desiderato è questo:
bye bye chao hello hola thank you gracias
Io stesso ho già escogitato cinque diversi approcci per risolvere questo problema. Ma penso che ci debbano essere più modi, probabilmente più concisi e più eleganti, e spero di poter imparare cose più interessanti :)
Quanto segue è quello che ho provato finora, in base a ciò che ho imparato dalle molte soluzioni dei miei precedenti problemi. Inoltre, sto cercando di digerire o interiorizzare le conoscenze che ho acquisito dal libro di Llama.
Codice 1:
#!perl
use autodie;
use warnings;
use strict;
open my $file1,'<','c:/file1.txt';
open my $file2,'<','c:/file2.txt';
while(defined(my $line1 = <$file1>)
and defined(my $line2 = <$file2>)){
die "Files are different sizes!\n" unless eof(file1) == eof(file2);
$line1 .= $line2;
$line1 =~ s/\n/ /;
print "$line1 \n";
}
Codice 2:
#!perl
use autodie;
use warnings;
use strict;
open my $file1,'<','c:/file1.txt';
my @file1 = <$file1>;
open my $file2,'<','c:/file2.txt';
my @file2 =<$file2>;
for (my $n=0; $n<=$#file1; $n++) {
$file1[$n] .=$file2[$n];
$file1[$n]=~s/\n/ /;
print $file1[$n];
}
Codice 3:
#!perl
use autodie;
use warnings;
use strict;
open my $file1,'<','c:/file1.txt';
open my $file2,'<','c:/file2.txt';
my %hash;
while(defined(my $line1 = <$file1>)
and defined(my $line2 = <$file2>)) {
chomp $line1;
chomp $line2;
my ($key, $val) = ($line1,$line2);
$hash{$key} = $val;
}
print map { "$_ $hash{$_}\n" } sort keys %hash;
Codice 4:
#!perl
use autodie;
use warnings;
use strict;
open my $file1,'<','c:/file1.txt';
open my $file2,'<','c:/file2.txt';
while(defined(my $line1 = <$file1>)
and defined(my $line2 = <$file2>)) {
$line1 =~ s/(.+)/$1 $line2/;
print $line1;
}
Codice 5:
#!perl
use autodie;
use warnings;
use strict;
open my $file1,'<','c:/file1.txt';
my @file1 =<$file1>;
open my $file2,'<','c:/file2.txt';
my @file2 =<$file2>;
while ((@file1) && (@file2)){
my $m = shift (@file1);
chomp($m);
my $n = shift (@file2);
chomp($n);
$m .=" ".$n;
print "$m \n";
}
Ho provato qualcosa del genere:
foreach $file1 (@file2) && foreach $file2 (@file2) {...}
Ma Perl mi ha dato un avviso di errore sintattico. Ero frustrato. Ma possiamo eseguire due foreach
loop contemporaneamente?
Grazie, come sempre, per eventuali commenti, suggerimenti e ovviamente per la generosa condivisione del codice :)
Soluzione
Funziona con qualsiasi numero di file:
use strict;
use warnings;
use autodie;
my @handles = map { open my $h, '<', $_; $h } @ARGV;
while (@handles){
@handles = grep { ! eof $_ } @handles;
my @lines = map { my $v = <$_>; chomp $v; $v } @handles;
print join(' ', @lines), "\n";
}
close $_ for @handles;
Altri suggerimenti
Il modo più elegante non prevede affatto perl
:
paste -d' ' file1 file2
Se fossi un golfista, potrei riscrivere @ Risposta di FM come:
($,,$\)=(' ',"\n");@_=@ARGV;open $_,$_ for @_;print
map{chomp($a=<$_>);$a} @_=grep{!eof $_} @_ while @_
che potresti essere in grado di trasformare in un one-liner ma che è semplicemente malvagio. ; -)
Bene, eccolo qui, con meno di 100 caratteri:
C:\Temp> perl -le "$,=' ';@_=@ARGV;open $_,$_ for @_;print map{chomp($a =<$_>);$a} @_=grep{!eof $_ }@_ while @_" file1 file2
Se è giusto bere (e perché diamine non & # 8212; noi sono alla ricerca di modi diversi), penso di aver scoperto il percorso la follia:
@_=@ARGV;chomp($x[$.-1]{$ARGV}=$_) && eof
and $.=0 while<>;print "@$_{@_}\n" for @x
C:\Temp> perl -e "@_=@ARGV;chomp($x[$.-1]{$ARGV}=$_) && eof and $.=0 while<>;print qq{@$_{@_}\n} for @x" file1 file2
Output:
bye bye chao hello hola thank you gracias
Un'alternativa più semplice al tuo codice 5 che consente un numero arbitrario di righe e non importa se i file hanno un numero diverso di righe (hat tip @FM):
#!/usr/bin/perl
use strict; use warnings;
use File::Slurp;
use List::AllUtils qw( each_arrayref );
my @lines = map [ read_file $_ ], @ARGV;
my $it = each_arrayref @lines;
while ( my @lines = grep { defined and chomp and length } $it->() ) {
print join(' ', @lines), "\n";
}
E, senza utilizzare alcun modulo esterno:
#!perl
use autodie; use warnings; use strict;
my ($file1, $file2) = @ARGV;
open my $file1_h,'<', $file1;
my @file1 = grep { chomp; length } <$file1_h>;
open my $file2_h,'<', $file2;
my @file2 = grep { chomp; length } <$file2_h>;
my $n_lines = @file1 > @file2 ? @file1 : @file2;
for my $i (0 .. $n_lines - 1) {
my ($line1, $line2) = map {
defined $_ ? $_ : ''
} $file1[$i], $file2[$i];
print $line1, ' ', $line2, "\n";
}
Se si desidera concatenare solo le righe visualizzate in entrambi i file:
#!perl
use autodie; use warnings; use strict;
my ($file1, $file2) = @ARGV;
open my $file1_h,'<', $file1;
my @file1 = grep { chomp; length } <$file1_h>;
open my $file2_h,'<', $file2;
my @file2 = grep { chomp; length } <$file2_h>;
my $n_lines = @file1 < @file2 ? @file1 : @file2;
for my $i (0 .. $n_lines - 1) {
print $file1[$i], ' ', $file2[$i], "\n";
}
Semplice con un controllo minimo degli errori:
#!/usr/bin/perl -w
use strict;
open FILE1, '<file1.txt';
open FILE2, '<file2.txt';
while (defined(my $one = <FILE1>) or defined(my $twotemp = <FILE2>)){
my $two = $twotemp ? $twotemp : <FILE2>;
chomp $one if ($one);
chomp $two if ($two);
print ''.($one ? "$one " : '').($two ? $two : '')."\n";
}
E no, non puoi eseguire due loop contemporaneamente nello stesso thread, dovresti fork
, ma non sarebbe garantito che funzionino in modo sincrono.