簡體   English   中英

在 Perl 中比較兩個文件中的數據

[英]Comparing data in two files in Perl

我有兩個文件,

文件A:

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

文件乙:

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};
 }

有兩個問題:解析您的數據格式,並進行比較。 您不能只是逐行比較文件,您的文件有一個結構,您需要將其解析為 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. 如果文件夾在兩個文件上,請顯示差異(如果有)。

幸運的是,我們有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);
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM