Сравните 4 файла построчно, чтобы увидеть, совпадают они или нет - PullRequest
0 голосов
/ 21 июня 2019

Я пытаюсь сравнить 4 текстовых файла для подсчета в каждой строке:

file1.txt:
32
44
75
22
88

file2.txt
32
44
75
22
88

file3.txt
11
44
75
22
77

file4.txt
    32
    44
    75
    22
    88

каждая строка представляет заголовок

line1 = customerID count
line2 = employeeID count
line3 = active_users
line4 = inactive_users
line5 = deleted_users

Я пытаюсь сравнить file2.txt , file3.txt и file4.txt с file1.txt ; file1.txt всегда будет иметь правильные значения.

Пример: поскольку file2.txt точно соответствует строка за строкой в ​​ file1.txt в приведенном выше примере, я пытаюсь вывести "file2.txt хорош «, но file3.txt line1 и line5 не соответствуют file1.txt Я пытаюсь вывести » customerID для file3.txt не совпадает с 21 запись ", ( т.е. 32 - 11 = 21 ) и " удаленных_пользователей в file3.txt не совпадает с 11 записями ", ( 88 - 77 = 11 ).

Если оболочка проще, то это тоже хорошо.

Ответы [ 5 ]

3 голосов
/ 21 июня 2019

Один способ параллельно обрабатывать файлы по линиям

use warnings;
use strict;
use feature 'say';

my @files = @ARGV;
#my @files = map { $_ . '.txt' } qw(f1 f2 f3 f4);  # my test files' names

# Open all files, filehandles in @fhs
my @fhs = map { open my $fh, '<', $_  or die "Can't open $_: $!"; $fh } @files;

# For reporting, enumerate file names
my %files = map { $_ => $files[$_] } 0..$#files;

# Process (compare) the same line from all files       
my $line_cnt;
LINE: while ( my @line = map { my $line = <$_>; $line } @fhs )
{
    defined || last LINE for @line;
    ++$line_cnt;
    s/(?:^\s+|\s+$)//g for @line;
    for my $i (1..$#line) {
        if ($line[0] != $line[$i]) { 
            say "File $files[$i] differs at line $line_cnt"; 
        }
    }
}

Сравнивает всю строку с == (после удаления пробелов в начале и в конце), так как нам дано, что каждая строка содержит одно число, которое необходимо сравнить.

Он печатает с моими тестовыми файлами с именем f1.txt, f2.txt, ...

File f3.txt differs at line 1
File f3.txt differs at line 5
1 голос
/ 22 июня 2019

Массив из bash, и в основном GNU версий стандартных утилит, таких как diff, sdiff, sed, и др. , а такжеifne util и даже eval:

f=("" "customerID count" "employeeID count" \
   "active_users" "inactive_users" "deleted_users")
for n in file{2..4}.txt ; do 
    diff -qws file1.txt $n || 
    $(sdiff file1 $n | ifne -n exit | nl | 
      sed -n '/|/{s/[1-5]/${f[&]}/;s/\s*|\s*/-/;s/\([0-9-]*\)$/$((&))/;p}' | 
      xargs printf 'eval echo "%s for '"$n"' does not match by %s records.";\n') ; 
done

Вывод:

Files file1.txt and file2.txt are identical
Files file1.txt and file3.txt differ
customerID count for file3.txt does not match by 21 records.
deleted_users for file3.txt does not match by 11 records.
Files file1.txt and file4.txt are identical

Тот же код, настроенный для более симпатичного вывода:

f=("" "customerID count" "employeeID count" \
   "active_users" "inactive_users" "deleted_users")
for n in file{2..4}.txt ; do 
    diff -qws file1.txt $n || 
    $(sdiff file1 $n | ifne -n exit | nl | 
      sed -n '/|/{s/[1-5]/${f[&]}/;s/\s*|\s*/-/;s/\([0-9-]*\)$/$((&))/;p}' | 
      xargs printf 'eval echo "%s does not match by %s records.";\n') ; 
done  | 
sed '/^Files/!s/^/\t/;/^Files/{s/.* and //;s/ are .*/ is good/;s/ differ$/:/}'

Выход:

file2.txt is good
file3.txt:
    customerID count does not match by 21 records.
    deleted_users does not match by 11 records.
file4.txt is good
1 голос
/ 22 июня 2019

Считать первый файл в массив, затем зациклить другие файлы, используя ту же функцию для чтения в массив. В этом цикле рассмотрите каждую строку, рассчитайте diff и напечатайте сообщение с текстом из @names, если diff не равно нулю.

#!/usr/bin/perl

use strict;
use warnings;

my @names = qw(customerID_count employeeID_count active_users inactive_users deleted_users);
my @files = qw(file1.txt file2.txt file3.txt file4.txt);

my @first = readfile($files[0]);

for (my $i = 1; $i <= $#files; $i++) {
    print "\n$files[0] <=> $files[$i]:\n";
    my @second = readfile($files[$i]);
    for (my $j = 0; $j <= $#names; $j++) {
        my $diff = $first[$j] - $second[$j];
        $diff = -$diff if $diff < 0;
        if ($diff > 0) {
            print "$names[$j] does not match by $diff records\n";
        }
    }
}

sub readfile {
    my ($file) = @_;
    open my $handle, '<', $file;
    chomp(my @lines = <$handle>);
    close $handle;
    return grep(s/\s*//g, @lines);
}

Вывод:

file1.txt <=> file2.txt:

file1.txt <=> file3.txt:
customerID_count does not match by 21 records
deleted_users does not match by 11 records

file1.txt <=> file4.txt:
1 голос
/ 22 июня 2019

Сохранить имена строк в массиве, сохранить правильные значения в другом массиве. Затем зациклите файлы и для каждого из них прочитайте их строки и сравните их с сохраненными правильными значениями. Вы можете использовать специальную переменную $., которая содержит номер строки последнего дескриптора файла доступа, чтобы служить индексом для массивов. Строки основаны на 1, массивы - на 0, поэтому нам нужно вычесть 1, чтобы получить правильный индекс.

#!/usr/bin/perl
use warnings;
use strict;
use feature qw{ say };

my @line_names = ('customerID count',
                  'employeeID count',
                  'active_users',
                  'inactive_users',
                  'deleted_users');

my @correct;
open my $in, '<', shift or die $!;
while (<$in>) {
    chomp;
    push @correct, $_;
}

while (my $file = shift) {
    open my $in, '<', $file or die $!;
    while (<$in>) {
        chomp;
        if ($_ != $correct[$. - 1]) {
            say "$line_names[$. - 1] in $file does not match by ",
                $correct[$. - 1] - $_, ' records';
        }
    }
}
0 голосов
/ 21 июня 2019

Вот пример на Perl:

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

{
    my $ref = read_file('file1.txt');
    my $N = 3;
    my @value_info;
    for my $i (1..$N) {
        my $fn = 'file'.($i+1).'.txt';
        my $values = read_file( $fn );
        push @value_info, [ $fn, $values];
    }
    my @labels = qw(customerID employeeID active_users inactive_users deleted_users);
    for my $info (@value_info) {
        my ( $fn, $values ) = @$info;
        my $all_ok = 1;
        my $j = 0;
        for my $value (@$values) {
            if ( $value != $ref->[$j] ) {
                printf "%s: %s does not match by %d records\n",
                  $fn, $labels[$j], abs( $value - $ref->[$j] );
                $all_ok = 0;
            }
            $j++;
        }
        say "$fn: is good" if $all_ok;
    }
}

sub read_file {
    my ( $fn ) = @_;

    my @values;
    open ( my $fh, '<', $fn ) or die "Could not open file '$fn': $!";
    while( my $line = <$fh>) {
        if ( $line =~ /(\d+)/) {
            push @values, $1;
        }
    }
    close $fh;
    return \@values;
}

Вывод :

file2.txt: is good
file3.txt: customerID does not match by 21 records
file3.txt: deleted_users does not match by 11 records
file4.txt: is good
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...