第一个文件名为W.txt,而2sd是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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
所以输出必须是这样的
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
Run Code Online (Sandbox Code Playgroud)
bri*_*foy 22
我想你正在尝试合并两个有相应记录的文件.我已经多次使用遗留系统看到了这个问题,其中不同的数据来自不同的来源.你必须确保所有记录排成一行(例如,在一个列表中没有添加或删除),但我们假设现在是正确的.
如果您习惯于处理面向行的文件(而不是宇宙中的所有内容),那么这是一项简单的任务.你从每个文件读取一行,删除行结束,连接两行,并将结果输出到第三个文件(虽然在这种情况下我使用标准输出):
#!/usr/bin/perl
use strict; # a programming aid to keep us honest
# open each file
open my $W, '<', 'W.txt' or die "Could not open W.txt: $!";
open my $Rs, '<', 'Rs.txt' or die "Could not open Rs.txt: $!";
# read the header of W.txt and ignore it
# this syncs the positions in the file
readline( $W );
while( 1 ) { # keep going until something else stops us
# read a line for each file
my $W_line = readline( $W );
my $Rs_line = readline( $Rs );
# stop if we ran out of lines from one of the files
last unless( defined $W_line and defined $Rs_line );
# remove the line ending from the W line
# leave the line ending on the Rs line because we'll use it
chomp( $W_line );
# output the combined line with a space between them
print $W_line, ' ', $Rs_line;
}
Run Code Online (Sandbox Code Playgroud)
我在这里添加了大量代码注释.当我正在处理我不确定的事情时,我经常在评论中勾勒出我想要的过程,然后填写代码来执行这些操作.如果您手动执行此操作,这大致就是您可能采取的过程.请记住,编程是使我们需要很长时间才能完成的无聊任务的自动化,因此步骤通常是相同的.实际上,有时候我会先手工做事来弄清楚过程中的问题.
但是,编程的真正诀窍是知道何时根本不需要编程.您想要合并两个文件.有一个程序:
% paste W.txt Rs.txt
Run Code Online (Sandbox Code Playgroud)
W.txt中的标题行存在问题.最简单的方法可能是简单地复制文件并删除那一行.如果您不必再次这样做,那么很少的手动干预可以为您节省大量工作:
% paste W-noheader.txt Rs.txt
Run Code Online (Sandbox Code Playgroud)
或者,您可以向Rs.txt添加一条虚线,以便它也有一个标题.您可能能够获取该数据的来源以添加该数据.为新值添加列标题会更好.另一个编程技巧是啤酒的应用.它润滑了许多问题.
如果你不在一台机器上paste(我不是在看你,Windows,但我认真对待),那就是一个名为Perl Power Tools的神奇项目,可以在Perl中重新创建工具,这意味着你可以在任何地方使用它们.你有一个perl,但你也可以看看它们是如何做到的.您可以使用接近您想要的工具,并根据您的本地目的稍微修改它.Perl在这里没什么特别的.如果你发现任何语言都很接近,那就去吧.诀窍是完成工作.
但是,假设您既不能手动编辑文件来删除标题(可能因为这必须是可重复的),也不能更改源以添加标题.您需要从不同的行开始同步文件.我认为paste应该处理这个问题,但没有版本我发现呢,我也想到了一个棘手的应用tail或head做.也许一个更好的Unix大师可以提供一个命令行.
Unix大师使用子进程提供了这样的命令行.这是来自Reddit的Rhombold:
要使用file2的内容粘贴file1减去第一行,您可以执行以下操作:
$ paste file1 <(tail -n +2 file2) >output
Run Code Online (Sandbox Code Playgroud)
您可以将此概括为任意数量的输入:
$ paste <(tail -n +10 file1) <(tail -n +3 file2) <(tail -n +7 file3) >output
Run Code Online (Sandbox Code Playgroud)
我已经给你完成任务的答案了,所以现在我要解决这个问题了.我想要一个改进的粘贴,让我指定每个文件的起始行.首先,我需要知道如何指定它.paste可以使用两个或更多文件,所以我也希望能够这样做.我需要能够为每个文件指定起始行号.我可以做这样的事情,我有一个起始行号列表,其顺序与我指定的文件相同.在这种情况下,逗号不是参数分隔符:
% epaste -l 1,2,3 file1 file2 file3
Run Code Online (Sandbox Code Playgroud)
我不喜欢这样,因为行号与文件分开; 这对我来说似乎很脏 我宁愿把它们放在一起.如果我必须从另一个程序构建此命令行,我不想跟踪行号并等到每个输入结束时知道如何输出命令.相反,通过允许文件名以"= N"结尾来指定起始行,我会做一些感觉有点脏的事情:
% epaste file1=1 file2=37 file3
Run Code Online (Sandbox Code Playgroud)
对于=名称中包含文件的文件存在问题,但生活很艰难.
看看Perl Power Tools版本的来源paste,我发现我真的只需要改变一个地方.当它打开文件时,我需要将文件"快进"到正确的起始行.目前的代码有:
for $i (0..$#ARGV) {
$fh[$i] = "F$i";
open($fh[$i], $ARGV[$i]) or die "$0: cannot open $ARGV[$i]";
}
Run Code Online (Sandbox Code Playgroud)
但我需要更改它来解析文件名以查找起始行号然后移动到该行号.
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
}
}
Run Code Online (Sandbox Code Playgroud)
这里有一些有趣的事情需要注意.在获取文件名的行中,我有这样的匹配:
$ARGV[$i] =~ /(.*?) (?: = ([0-9]+) )? \z/x;
Run Code Online (Sandbox Code Playgroud)
除了换行符之外,我有一个非贪婪的匹配任何字符,(.*?)然后是一个选项部分,用于查找等号,后跟一系列十进制数字(?: = ([0-9]+) )?,但仅限于最后\z.该/x让我散播出在模式微不足道制作空白.
如果我匹配的东西,$line有一个值.如果我不这样做,那$line就是undef.如果有什么东西,我只需要快进$line.我使用defined来检查它.
if( defined $line ) {
...
}
Run Code Online (Sandbox Code Playgroud)
在里面if,我需要停在正确的行号.如果我想从第37行开始,我需要阅读并丢弃36行.这比我指定的数字少一个.
为此,我可以看一下$.,最近读取的文件的当前行号(在perlvar中记录.注意"最近阅读".我没有读过我正在使用的文件,但我可以使用tell更改$.为我刚刚打开的文件句柄而不读取数据:
tell( $fh[$i] );
readline( $fh[$i] ) while $. < $line - 1
Run Code Online (Sandbox Code Playgroud)
James在blogs.perl.org评论说我可以使用文件句柄作为对象并完全避免使用特殊变量:
... $fh->input_line_number < $line - 1
Run Code Online (Sandbox Code Playgroud)
那是我的Perl 4显示出来的.请注意,您可能必须包含use FileHandlev5.12及更早版本的代码,因为直到v5.14才会将其添加为默认值.
几乎就是这样.我不会看到程序的其余部分以及处理其他功能的棘手事情paste,例如更改分隔符.
为了继续支持特殊文件名-作为标准输入的名称,我需要稍微调整处理选项,这样它就不会认为=是一个选项(我在这里没有显示):
% epaste -=3 W.txt
Run Code Online (Sandbox Code Playgroud)
我希望我可以-多次指定一个起始行号,但是这些都是相互依赖的,因为它们使用相同的数据.我可以同时指定多个文件(如果您的文件系统允许同时读取文件):
% epaste animals.txt=2 animals.txt=6 animals.txt=4
Run Code Online (Sandbox Code Playgroud)
这意味着您的解决方案归结为:
% epaste W.txt=2 Rs.txt
Run Code Online (Sandbox Code Playgroud)
我为那些想要文件或修正错误的人制作了一个史诗要点,以修复我所犯的错误.
而且,这是当天的最终编程技巧:让其他人编写程序.:)