【问题标题】:Comparing three files in Perl在 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 中做事总是好的。尤其是交叉检查。 @肖恩
  • @罗伯特哇。我不敢相信我没想过把它放进一个潜艇。那肯定会让它更干净。

标签: perl


【解决方案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;
        }
    }
}

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

【讨论】:

    猜你喜欢
    • 2015-07-08
    • 2015-01-16
    • 2013-09-13
    • 2014-02-16
    • 1970-01-01
    • 2014-06-14
    • 2018-09-05
    • 2018-08-17
    • 2012-03-18
    相关资源
    最近更新 更多