如何使用Perl将文件的元素作为列添加到第二个文件中?

如何使用Perl将文件的元素作为列添加到第二个文件中?,perl,Perl,第一个文件名为W.txt,第二个文件名为Rs.txt W.txt: ID age gender bmi status CAD7 57 F 28.80 0 CAD9 74 F 29.26 1 CAD11 53 M NA 1 CAD12 61 M 27.16 1 CAD14 77 M 29.28 1 CAD17 74 M 35.99 1 CAD18 81 F 28.12 1 CAD24 7

第一个文件名为W.txt,第二个文件名为Rs.txt

W.txt:

ID  age gender  bmi status  
CAD7    57  F   28.80   0
CAD9    74  F   29.26   1
CAD11   53  M   NA  1
CAD12   61  M   27.16   1
CAD14   77  M   29.28   1
CAD17   74  M   35.99   1
CAD18   81  F   28.12   1
CAD24   73  M   22.23   1
Rs.txt:

2   2   2   
2   2   2   
2   0   2   
2   2   2   
1   2   2   
1   2   2   
1   2   2   
1   2   2   
所以输出必须是这样的

CAD7    57  F   28.80   0   2   2   2   
CAD9    74  F   29.26   1   2   2   2
CAD11   53  M   NA  1   1   2   2   

只要求密码是很不礼貌的

您可以通过将整个文件读入一个字符串,然后按
“\n”
拆分字符串,将其推入数组,然后按如下方式打印每个元素:

#!/usr/bin/perl
use strict;
use warnings;

open W_FILE, "./W.txt" or die $!;
open R_FILE, "./R.txt" or die $!;
my $w_content;
my $r_content;

while(<W_FILE>) {
  $w_content .= $_;
}
close(W_FILE);
while(<R_FILE>) {
  $r_content .= $_;
}
close(R_FILE);

my @w_array = split(/\n/, $w_content);
my @r_array = split(/\n/, $r_content);

my $i;
for($i = 0; $i < $#w_array; $i ++) {
  print $w_array[$i+1]." ".$r_array[$i]."\n";
}
#/usr/bin/perl
严格使用;
使用警告;
打开W_文件“./W.txt”或die$!;
打开R_文件“./R.txt”或骰子$!;
我的$w_内容;
我的$r_内容;
while(){
$w_内容=$\u;
}
关闭(W_文件);
while(){
$r\u内容=$\u;
}
关闭(R_文件);
my@w_数组=拆分(/\n/,$w_内容);
my@r\u数组=拆分(/\n/,$r\u内容);
我的$i;
对于($i=0;$i<$#w#u数组;$i++){
打印$w_数组[$i+1]。“”.$r_数组[$i]。“\n”;
}

我认为您正在尝试合并两个具有相应记录的文件。我已经在遗留系统中多次看到这个问题,其中不同的数据来自不同的来源。您必须确保所有记录都排列整齐(例如,一个列表中没有添加或删除记录),但现在我们假设这是真的

如果您习惯于处理面向行的文件(而不是世界上所有的文件),那么这是一项简单的任务。从每个文件中读取一行,删除行尾,连接两行,并将结果输出到第三个文件(尽管在本例中我使用标准输出):

W.txt中的标题行有问题。最简单的方法可能是简单地复制文件并删除这一行。如果您不必再次执行此操作,少量手动干预可以为您节省大量工作:

% paste W-noheader.txt Rs.txt
或者,您可以向Rs.txt添加一个虚拟行,使其也有一个标题。您可能可以获取该数据的源以添加该数据。为新值设置列标题会更好。另一个编程技巧是啤酒的应用。它缓和了许多问题

如果您不是在一台有
粘贴
的机器上(我不是在看您,Windows,但我是认真的),那么有一个很棒的项目叫做,它用Perl重新创建工具,这意味着您可以在任何有
Perl
的地方使用它们,而且您还可以查看源代码,看看它们是如何做到的。您可以使用一个接近您想要的工具,并根据您的本地目的对其进行轻微修改。Perl在这里没有什么特别之处。如果你在任何一种语言中发现了一些相近的东西,那就去做吧。诀窍是完成工作

但是,假设您既不能手动编辑文件以删除头(可能是因为这必须是可重复的),也不能更改源以添加头。您需要从不同的行开始同步文件。我认为
paste
应该能处理这个问题,但我发现没有一个版本能处理这个问题,我还认为
tail
head
的一个棘手的应用程序能处理这个问题。也许更好的Unix专家可以提供一个命令行


一位Unix专家使用子进程提供了这样的命令行:

要使用file2内容减去第一行粘贴file1,可以执行以下操作:

$ paste file1 <(tail -n +2 file2) >output
我不喜欢这样,因为行号与文件是分开的;我觉得它很脏。我宁愿让他们在一起。如果我必须从另一个程序建立这个命令行,我不想跟踪行号,并等到每个输入结束后才知道如何输出命令。相反,我将通过允许文件名以“=N”结尾来指定起始行,从而执行一些感觉有点脏的操作:

% epaste file1=1 file2=37 file3
对于名称中带有
=
的文件,这有一个问题,但生活很艰难

查看源代码,我发现实际上只有一个地方需要更改。当它打开文件时,我需要将文件“快进”到正确的起始行。当前代码包含以下内容:

for $i (0..$#ARGV) {
    $fh[$i] = "F$i";
    open($fh[$i], $ARGV[$i]) or die "$0: cannot open $ARGV[$i]";
}
但是我需要修改它来解析文件名,以查找起始行号,然后移动到该行号

for $i (0..$#ARGV) {
    $fh[$i] = "F$i";
    my( $name, $line ) = $ARGV[$i] =~ /(.*?) (?: = ([0-9]+) )? \z/x;
    open($fh[$i], $name) or die "$0: cannot open $name";
    if( defined $line ) {
        tell( $fh[$i] );
        readline( $fh[$i] ) while $. < $line - 1
    }
}
我对任何字符都有一个非贪婪匹配,除了换行符,
(.*?
,后跟一个选项部分,用于查找一个等号,后跟一系列十进制数字
(?:=([0-9]+)?
,但仅在
\z
末尾。
/x
让我们通过使模式中的空白变得无关紧要来扩展它

如果我匹配某个内容,
$line
有一个值。如果没有,则,
$line
具有undef。如果
$line
中有内容,我只需要快进。我过去经常检查

    if( defined $line ) {
        ...
    }
在该
中,如果
,我需要在正确的行号处停止。如果我想从第37行开始,我需要阅读并丢弃36行。比我指定的数字少一个

为此,我可以查看
$。
,最新读取的文件的当前行号(记录在中)。请注意,“最近读取”。我还没有读取正在使用的文件,但我可以使用它将
$。
更改为刚打开的文件句柄,而不读取数据:

        tell( $fh[$i] );
        readline( $fh[$i] ) while $. < $line - 1
我希望我可以多次使用起始行号指定
-
,但这些行号都会相互影响,因为它们使用相同的数据。我可以同时指定多个文件(如果文件系统允许同时读取文件):

这意味着您的解决方案可以归结为:

% epaste W.txt=2 Rs.txt
我已经为那些想要该文件或有更正来修复我犯下的错误的人做了一次尝试


这就是今天的最后一个编程技巧:让其他人编写程序。

假设索引值是唯一的,并且数据适合内存,我只会使用数组散列

use strict;
use warnings;

my $data_hash_ref;  #store the data here

open (my $w_fh, "<", "W.txt") or die $!;

#skip title line
my $line = <$w_fh>;

while ($line = <$w_fh>) {
    chomp $line;
    my @cols = split ("\t", $line);
    my $key = shift (@cols);
    $data_hash_ref -> {$key} = \@cols;
}
close $w_fh;

open (my $rs_fh, "<", "Rs.txt") or die $!;

while ($line = <$Rs_fh>) {
    chomp $line;
    my @cols = split ("\t", $line);
    my $key = shift (@cols);

    #You probably want to check if the key exists first and handle it if it doesn't,
    #but I'm skipping that here

    push (@{$data_hash_ref -> {$key}}, @cols);
}
close $rs_fh;

#print it out
open (my $out_fh, ">", "merged.txt") or die $!;

foreach my $key (sort keys %$data_hash_ref) {
    my $row = join ("\t", @{$data_hash_ref -> {$key}})
    print $out_fh "$key\t$row\n";
}
close $out_fh;
使用严格;
使用警告;
我的$data\u hash\u ref#将数据存储在这里
打开(my$w_fh,“,”merged.txt“)或死亡$!;
foreach my$key(排序键%$data\u hash\u ref){
my$row=join(“\t”,@{$data\u hash\u ref->{$key})
打印$out\u fh“$key\t$row\n”;
}
关闭$OFH;
不像某些解决方案那样优雅,但如果您不使用
$ARGV[$i] =~ /(.*?) (?: = ([0-9]+) )? \z/x;
    if( defined $line ) {
        ...
    }
        tell( $fh[$i] );
        readline( $fh[$i] ) while $. < $line - 1
... $fh->input_line_number < $line - 1
% epaste -=3 W.txt
% epaste animals.txt=2 animals.txt=6 animals.txt=4
% epaste W.txt=2 Rs.txt
use strict;
use warnings;

my $data_hash_ref;  #store the data here

open (my $w_fh, "<", "W.txt") or die $!;

#skip title line
my $line = <$w_fh>;

while ($line = <$w_fh>) {
    chomp $line;
    my @cols = split ("\t", $line);
    my $key = shift (@cols);
    $data_hash_ref -> {$key} = \@cols;
}
close $w_fh;

open (my $rs_fh, "<", "Rs.txt") or die $!;

while ($line = <$Rs_fh>) {
    chomp $line;
    my @cols = split ("\t", $line);
    my $key = shift (@cols);

    #You probably want to check if the key exists first and handle it if it doesn't,
    #but I'm skipping that here

    push (@{$data_hash_ref -> {$key}}, @cols);
}
close $rs_fh;

#print it out
open (my $out_fh, ">", "merged.txt") or die $!;

foreach my $key (sort keys %$data_hash_ref) {
    my $row = join ("\t", @{$data_hash_ref -> {$key}})
    print $out_fh "$key\t$row\n";
}
close $out_fh;