0

私は Perl の初心者で、これを行うのに苦労しています。単一列の CSV ファイルが 2 つあり、違いを 3 つ目のファイルに出力しようとしています。

File1:
123
124
125
126

File2:
123
124
127

Expected Output:
125
126
127

これは私がこれまでに得たものであり、機能していません:

#!/usr/bin/perl

use strict;
use warnings;

my $sheet_1;
my $sheet_2;
my $count1 = 0;
my $count2 = 0;

my $file1 = 'file1.csv';
my $file2 = 'file2.csv';
my $file_out = 'output.csv';

open (FILE1, "<$file1")  or die "Couldn't open input file: $!"; 
open (FILE2, "<$file2")  or die "Couldn't open input file: $!"; 


while( <FILE1> ) {
  chomp;
  $count1++;
  #skip header;
  next unless $count1;
  my $row_1;
  @$row_1 = split( /,/, $_ );
  push @$sheet_1, $row_1;
}
@$sheet_1 = sort { $a->[0] <=> $b->[0] } @$sheet_1;

while( <FILE2> ) {
  chomp;
  $count2++;
  #skip header;
  next unless $count2;
  my $row_2;
  @$row_2 = split( /,/, $_ );
  push @$sheet_2, $row_2;
}

@$sheet_2 = sort { $a->[0] <=> $b->[0] } @$sheet_2;


OUTER: {
     foreach my $row_1 ( @$sheet_1 ) {
         foreach my $row_2 ( @$sheet_2 ) {
        if (@$row_1[0] eq @$row_2[0]){
        last OUTER
        }
        else{
        print "@$row_1[0]\n";
        }
        }
    }
}

close FILE1;
close FILE2;
4

3 に答える 3

2

diffcommを見てください。これらはあなたが望むことをするかもしれません。

いくつか質問があります。

  1. これらのファイルが行ごとに 1 つの値しか持たない場合、それらが CSV ファイルになる理由は何ですか? CSV ファイルには、カンマで区切られた複数の列があります (CSV = カンマ区切り値)。何か他に何かありますか。
  2. 両方のファイルの値が同じであるが、2 つの異なる場所にある場合、それを差分としてカウントしますか? 1 つのファイルに 3 行があり、その行に1, 2, 3. その中の 2 番目のファイルと比較して1, 3, 2います。2行目と3行目が違う?または、同じ値が含まれているため、ファイルは同じですか?

いいえ、両方のファイルの異なる場所に同じ値がある場合、この値は出力に含まれるべきではありません。あなたの例では、2 つのファイル (1,2,3) と (1,3,2) は同じです。– Yoboy 7時間前

結構...

グループ #2 タイプの質問でグループ #1 の項目があるときはいつでも、ハッシュを考える必要があります。

ハッシュは、各値がキーを持つ値のリストです。リスト内で値が重複する可能性がありますが、特定のキーのインスタンスは 1 つだけです。これは、キーが既にリストにあるかどうかを簡単に確認できることを意味します。

ファイル #1 を取得し、各値をキーとしてハッシュに入れることを想像してください。値が何であるかは関係ありません。単にキーに関心があるだけです。

ここで、ファイル #2 を調べると、そのキーが既にハッシュに含まれているかどうかをすぐに確認できます。そうであれば、それは重複値です。

ハッシュの 2 番目の機能を利用することもできます: キーのインスタンスは 1 つしか許可されません。両方のハエを 1 つのハッシュに入れるとどうなるでしょうか? ファイル #1 とファイル #2 の間で値が重複していても問題ありません。そのキーのインスタンスは 1 つだけです。

両方のファイルで一意の値のリストを取得する方法は次のとおりです。

use strict;
use warnings;
use feature qw(say);
use autodie;

use constant {
    FILE_1  => "file1.txt",
    FILE_2  => "file2.txt",
};

my %hash;
#
# Load the Hash with value from File #1
#
open my $file1_fh, "<", FILE_1;
while ( my $value = <$file1_fh> ) {
    chomp $value;
    $hash{$value} = 1;
}
close $file1_fh;
#
# Add File #2 to the Hash
#
open my $file2_fh, "<", FILE_2;
while ( my $value = <$file2_fh> ) {
    chomp $value;
    $hash{$value} = 1;   #If that value was in "File #1", it will be "replaced"
}
close $file2_fh;

#
# Now print out everything
#
for my $value ( sort keys %hash ) {
    say $value;
}

これは出力されます:

123
124
125
126
127

必要なのは、一意の値のリストです。これは、最初に思われるよりも少しトリッキーです。ファイル #1 の値をハッシュに入れ、ファイル #1 にない場合はファイル #2 の値を出力できます。これにより、ファイル #2 の一意の値のリストが得られますが、ファイル #1 の一意の値は得られません。

したがって、ファイル #1 用とファイル #2 用に 1 つずつ、合計 2 つのハッシュを作成し、それぞれを調べて相互に比較する必要があります。

use strict;
use warnings;
use feature qw(say);
use autodie;

use constant {
    FILE_1  => "file1.txt",
    FILE_2  => "file2.txt",
};

#
# Load Hash #1 with value from File #1
#
my %hash1;
open my $file1_fh, "<", FILE_1;
while ( my $value = <$file1_fh> ) {
    chomp $value;
    $hash1{$value} = 1;
}
close $file1_fh;

#
# Load Hash #2 with value from File #2
#
my %hash2;
open my $file2_fh, "<", FILE_2;
while ( my $value = <$file2_fh> ) {
    chomp $value;
    $hash2{$value} = 1;
}
close $file2_fh;

ここで、一方を他方と比較する必要があります。今のところ、値を配列に格納します。

my @array;
#
# Check if File #1 has unique values vs File #2
#
for my $value ( %keys %hash1 ) {
   if ( not exists $hash2{$value} ) {
      push @array, $value;  #Value in File #1, but not in File #2
   }
}
#
# Check if File #2 has unique values vs File #1
#
for my $value ( %keys %hash2 ) {
   if ( not exists $hash1{$value} ) {
      push @array, $value;  #Value in File #2, but not in File #1
   }
}
#
# Now print out what's in @array of unique values
#
for my $value ( sort @array ) {
    say $value;
}
于 2013-06-20T17:55:01.023 に答える
2

Text::Diff Perl モジュールを使用してこれを行うことができます。それ以外の場合は、以下を参照してください。

比較を行う 1 つのアルゴリズムを次に示します。

use strict;
my @arr1;
my @arr2;
my $a;

open(FIL,"a.txt") or die("$!");
while (<FIL>)
    {chomp; $a=$_; $a =~ s/[\t;, ]*//g; push @arr1, $a if ($a ne  '');};
close(FIL);

open(FIL,"b.txt") or die("$!");
while (<FIL>)
    {chomp; $a=$_; $a =~ s/[\t;, ]*//g; push @arr2, $a if ($a ne  '');};
close(FIL);

my %arr1hash;
my %arr2hash;
my @diffarr;
foreach(@arr1) {$arr1hash{$_} = 1; }
foreach(@arr2) {$arr2hash{$_} = 1; }

foreach $a(@arr1)
{
    if (not defined($arr2hash{$a})) 
     {
        push @diffarr, $a;
     }
}

foreach $a(@arr2)
{
   if (not defined($arr1hash{$a})) 
   { 
       push @diffarr, $a;
   }
}

print "Diff:\n";
foreach $a(@diffarr)
{
    print "$a\n";
}
# You can print to a file instead, by: print FIL "$a\n";
于 2013-06-20T17:20:40.250 に答える
0
  1. 単一列の場合、分割するコンマはありません。どうしてそんなことをするのか?「\n」でファイルを分割するだけです
  2. 車輪を再発明しないでください。複数の列を持つ実際の CSV の場合は、 Text::CSV::Slurp のようなものを使用して読み取ります
  3. アイテムを探すときに各ファイル全体をループする代わりに、ハッシュを使用してルックアップを行います。ただし、大きなファイルを扱っている場合は、メモリの問題が発生する可能性があります。

すなわち:

use strict;
use warnings;
use 5.012;

use Text::CSV::Slurp;

my $file1_src=<<EOF;
id,field1,field2,field3
123,junk,"quoted junk",junk 
124,"quoted junk","quoted junk",junk 
125,junk,"quoted junk",junk 
126,junk,"quoted junk",junk 
EOF

my $file2_src=<<EOF;
id,field1,field2,field3
123,junk,"quoted junk",junk 
124,junk,"quoted junk",junk 
127,"quoted junk","quoted junk",junk
EOF

my %data1 = map { $_->{id} => 1 } @{Text::CSV::Slurp->load(string => $file1_src)};
my %data2 = map { $_->{id} => 1 } @{Text::CSV::Slurp->load(string => $file2_src)};

for my $id (keys %data1, keys %data2) {
  say $id unless $data1{$id} and $data2{$id};
}
于 2013-06-20T16:57:29.877 に答える