在 Perl 中比较三个文件

Posted

技术标签:

【中文标题】在 Perl 中比较三个文件【英文标题】:Comparing three files in Perl 【发布时间】:2019-12-20 08:00:45 【问题描述】:

我有三个包含姓名和成绩的文本文件。我删除了成绩并仅使用名称创建了新文件。以下是文件的样子:

first.txt

爱丽丝 鲍勃 卡尔 井架 杰西卡 莎拉 扎克

second.txt

爱丽丝 鲍勃 井架 贾里德 杰西卡 莎拉 扎克

第三个.txt

鲍勃 贾里德 莎拉 板岩 特里 扎克

我想比较所有三个文件,如果一个文件中的名称不在另一个文件中,我想添加它。所以最后所有文件都将包含相同的名称。我知道你会在 perl 中添加行,所以必须创建一个新文件来执行此操作。

这是我的方法。我首先比较第一个和第二个,然后将第二个的差异添加到第一个中。然后比较第一和第二,从第一到第二添加差异。然后我将第二个文件(任何一个工作)与第三个文件进行比较,将第二个文件的差异打印到第三个文件中。然后我比较第二和第三,并将第三的差异打印到第一和第二。我也放入了比较语句以确保文件具有相同的条目。

带有成绩的文件命名为original1.txtoriginal2.txtoriginal3.txt

最后,我将获取包含新名称的文件,并将它们与具有等级的文件结合起来。如果文件中没有新名称的成绩,则它根本没有成绩条目。

有没有更清洁的方法来做到这一点?它看起来像一个巨大的混乱。

【问题讨论】:

你需要使用perl吗?这可能只是几行 shell - 鉴于文件已经排序,sort -um first.txt second.txt third.txt 将给出所有文件的所有名称。 (如果尚未排序,请删除 -m)。然后join与成绩文件合并... use strict; use warnings; 使用适当的缩进。将重复的代码片段移动到subs。 整个脚本都在 perl 中,所以我尽量在纯 perl 中完成它,因为我的代码中有几个 sed 和 awk。不过,我也很欣赏你的建议。知道如何在 shell 中做事总是好的。尤其是交叉检查。 @肖恩 @罗伯特哇。我不敢相信我没想过把它放进一个潜艇。那肯定会让它更干净。 【参考方案1】:

除非这是针对一个类或某些使用 perl 是硬性要求的东西,否则更简洁的方法是根本不使用 perl,而是使用标准的 shell 实用程序。

假设您的 originalN.txt 文件如下所示:

Alice   A
Bob     B
Carl    C
Derrick D
Jessica A
Sarah   B
Zach    C

用标签分隔列

你可以这样做:

sort -um <(cut -f1 original1.txt) \
         <(cut -f1 original2.txt) \
         <(cut -f1 original3.txt) > allnames.txt

要从所有三个文件中获取包含所有名称的文件(如果它们尚未按名称排序,请改用sort -u ...)。对于 &lt;(command) 重定向语法,这确实需要 bash、zsh 或 ksh93。

然后您可以将这些名称与每个单独的文件合并,并带有一个左外部join

$ join -t$'\t' -a1 allnames.txt original1.txt
Alice   A
Bob     B
Carl    C
Derrick D
Jared
Jessica A
Sarah   B
Slate
Terry
Zach    C

等等。


如果使用 perl,则不需要所有这些临时文件。只需将所有原始文件的名称粘贴在哈希中即可:

#!/usr/bin/env perl
use warnings;
use strict;
use autodie;
use feature qw/say/;

# Read all names from the files given on the command line.
my %names;
for my $file (@ARGV) 
    open my $infile, "<", $file;
    while (<$infile>) 
        my $n = ( split /\t/ )[0];
        $names$n = 1;
    


# And for each file, merge with all the names
for my $file (@ARGV) 
    say "****** $file *******";
    open my $infile, "<", $file;
    my %grades = map  $_ => undef  keys %names;
    while (<$infile>) 
        chomp;
        my ( $name, $grade ) = split /\t/;
        $grades$name = $grade;
    
    for my $name ( sort keys %grades ) 
        if ( defined $grades$name ) 
            say "$name\t$grades$name";
        
        else 
            say $name;
        
    

将结果写入文件而不是标准输出留给读者作为练习。

【讨论】:

以上是关于在 Perl 中比较三个文件的主要内容,如果未能解决你的问题,请参考以下文章

perl 的 XML::SemanticDiff 可以硬塞到两个 XML 文件的顺序不可知比较中吗?

如何从 Perl 中的一行中提取非空白组?

Perl:匹配文件中的正则表达式

perl 第三弹 句柄

比较 2 个 CSV 巨大的 CSV 文件并使用 perl 将差异打印到另一个 csv 文件

在perl中计算汉明距离