比较Perl中两个文件中的数据

时间:2015-09-21 20:24:22

标签: perl

我有两个文件,

档案A:

Folder name A
   cp A
   cp B
Folder name B
   cp D
   cp F

档案B:

Folder name C
    cp A
    cp B
Folder name A
    cp A
    cp B
    cp C
Folder name B
    cp D
    cp F
Folder name D
    cp A
    cp D

输出应为:

Folder name C:
     cp A
     cp B
Folder name D
     cp A
     cp D
Folder name A
     cp C

基本上,我想检查文件夹名称中是否匹配,然后检查cp名称中是否存在相同文件夹名称的匹配项。然后我们需要删除匹配项。任何人都可以帮助我,因为我不熟悉perl。

我有代码,它正确地给出文件夹名称,但删除了一些cp名称。

my %file2;
open my $file2, '<', 'fileA.txt' or die "Couldnt open fileA.txt";
while (my $line = <$file2>)
{
   ++$file2{$line};
 }
open my $file1, '<', 'fileB.txt' or die "Couldnt open fileB.txt";
while (my $line = <$file1>)
{
   print $fh $line unless $file2{$line};
 }

1 个答案:

答案 0 :(得分:0)

有两个问题:解析数据格式和进行比较。您不能逐行比较文件,您的文件有结构,您需要将其解析为Perl数据结构。

sub parse_file {
    my $file = shift;

    open my $fh, '<', $file;

    my $in_folder;
    my %folders = ();

    while(<$fh>) {
        # Entering a folder
        if( /^Folder name (.*)\s*$/ ) {
            $in_folder = $1;
        }
        # We're in a folder
        elsif( $in_folder ) {
            # Add a line to the folder actions
            if( /^\s+(.*)\s*$/ ) {
                push @{$folders{$in_folder}}, $1;
            }
            # We exited the folder but didn't enter another one
            elsif( /^\S/ ) {
                $in_folder = '';
            }
        }
    }

    return \%folders;
}

这是编写和调试的大量额外代码。如果您的文件存储在YAML,JSON或XML之类的内容中,您可以使用库来执行此操作。

我故意选择去除格式化并只存储文件夹名称。这使得数据更易于使用,并保护代码的其余部分不会格式化更改。

现在每个文件都是包含命令列表的文件夹名称的散列。

      {
        'A' => [
                 'cp A',
                 'cp B'
               ],
        'B' => [
                 'cp D',
                 'cp F'
               ]
      }

现在我们需要比较它们。算法是这样的:

  1. 如果文件夹只在一个文件中,请选择它。
  2. 如果两个文件都有文件夹,请显示差异(如果有)。
  3. 幸运的是,我们Array::Utils为我们做了所有必要的交集和差异。使用array_diff查找仅在一个文件中的文件夹,并intersection查找两个文件夹中的文件夹。然后再次使用array_diff查找差异。

    sub compare_folders {
        my($set1, $set2) = @_;
    
        my @set1_names = keys %$set1;
        my @set2_names = keys %$set2;
    
        my %diffs;
    
        # It's in one but not the other.
        for my $name (array_diff @set1_names, @set2_names) {
            $diffs{$name} = $set1->{$name} || $set2->{$name};
        }
    
        # It's in both.
        for my $name (intersect @set1_names, @set2_names) {
            # They're different
            if( my @diff = array_diff(@{$set1->{$name}}, @{$set2->{$name}}) ) {
                $diffs{$name} = \@diff;
            }
        }
    
        return \%diffs;
    }
    

    最后我们需要显示结果。由于我选择将数据设为通用并删除格式,我们需要将其放回原位。

    sub display_folder {
        my($name, $values) = @_;
    
        my $display = "Folder name $name\n";
    
        for my $value (@$values) {
            $display .= "    $value\n"
        }
    
        return $display;
    }
    

    并坚持下去。

    my @folders = map { parse_file($_) } @ARGV;
    
    my $diff = compare_folders(@folders);
    
    for my $name (keys %$diff) {
        my $values = $diff->{$name};
        print display_folder($name, $values);
    }