Сравнение CSV файлов для поиска в первом столбце, затем проверка значений во втором столбце.

Вопрос или проблема

Хорошо, я постараюсь объяснить, что мне нужно сделать, как можно лучше. В основном у меня есть два CSV файла, как указано в примерах ниже:

Файл 1:

Столбец 1, Столбец 2
abc     , 123
def     , 234
adf     , 567

Файл 2:

Столбец 1, Столбец 2
abc     , 123
def     , 234
adf     , 578

Мне нужно написать либо оболочку, либо простую команду, которая выполнит следующее:

  1. Отсортируйте оба файла по столбцу 1
  2. По строкам выполните следующее:
    • Используя столбец 1 из файла 1, ищите это значение в столбце 1 из файла 2.
      1. если найдено, сравните значение в столбце 2 файла 1 со значением в столбце 2 файла 2
      2. если совпадает, запишите столбец 1, столбец 2 и “Подтверждено” в столбец 3 в отдельный файл
      3. если не совпадает, запишите столбец 1, столбец 2 и “Неудача” в отдельный файл

В результате получается два выходных файла: первый с тем, что было найдено в совпадениях столбца 1 и столбца 2, и второй файл, содержащий либо трудности с поиском столбца 1, либо случаи, когда столбец 1 был найден, но столбец 2 не совпадал, по сути, используя столбец 1 в качестве ключа для проверки столбца 2.

Исходя из следующих входных файлов:

$ cat in1 in2
Столбец 1, Столбец 2
abc     , 123
def     , 234
adf     , 567
Столбец 1, Столбец 2
abc     , 123
def     , 234
adf     , 578

Сначала мы их сортируем; затем мы можем объединить их в один файл:

$ sort in1 > in1.sorted; sort in2 > in2.sorted; paste in{1,2}.sorted
Столбец 1, Столбец 2  Столбец 1, Столбец 2
abc     , 123   abc     , 123
adf     , 567   adf     , 578
def     , 234   def     , 234

awk поможет нам здесь, но запятые мешают; сначала мы можем избавиться от них с помощью sed:

$ paste in{1,2}.sorted | sed s/,//g
Столбец 1 Столбец 2   Столбец 1 Столбец 2
abc      123    abc      123
adf      567    adf      578
def      234    def      234

А затем мы можем пропустить это через быстрый awk:

$ paste in{1,2}.sorted | sed s/,//g | awk '$2 == $4 {print $1,"Подтверждено"}; $2 != $4 { print $1,"Неудача"}'
Столбец Неудача
abc Подтверждено
adf Неудача
def Подтверждено

Это также можно сделать с использованием awk, с той выгоды, что мы можем убрать строки заголовка и не полагаться на то, что одни и те же данные будут в одном и том же порядке, таким образом убирая необходимость в сортировке:

$ awk 'FNR != 1 && NR == FNR {data[$1]=$3} FNR != 1 && NR != FNR {if(data[$1]==$3) {print $1, "Подтверждено"} else {print $1, "Неудача"} }' in{1,2}
abc Подтверждено
adf Неудача
def Подтверждено

Это основывается на нескольких магических встроенных переменных и трюках, связанных с ними:

  • NR – общее количество обработанных записей
  • FNR – общее количество записей в текущем файле обработанных
  • FNR != 1 – пропускает первую строку каждого файла (не рассматривает заголовки как данные)
  • NR != FNR – работает только после того, как первый файл был полностью прочитан и мы начали читать последующие файлы. Это позволяет нам предварительно заполнить массив data для проверки, как только мы начнем обрабатывать второй файл.

Мне кажется, что я разобрался с этим с помощью следующего, на случай, если кто-то еще прочитает это и нуждается в этом. Спасибо снова.

FNR == NR {
    for (i = 2; i <= NF; i++) { a[i,$1] = $i }
    b[$1];
    next;
}
($1 in b) {                   # проверка, существует ли строка в файле2 в файле1
    for (i = 2; i <= NF; i++) {
        if (a[i,$1] == $i)
            printf("%s->col%d: %s vs %s: Подтверждено\n", $1, i-1, a[i,$1], $i);
        else
            printf("%s->col%d: %s vs %s: Неудача\n", $1, i-1, a[i,$1], $i);
    }
    delete b[$1];   # удалить записи, которые были обработаны
}

END {
    for (left in b) {   # посмотреть, что не совпадало
        for (i = 2; i <= NF; i++) 
            printf("%s->col%d: %s vs (пусто): Не равно\n", left, i-1, a[i,left])
    }
}

Используя Raku (ранее известный как Perl_6)

#! /usr/bin/env raku

    #ВВОД И ЗАГОЛОВКИ:

    my $csv1 = "Veyron1.txt".IO;
    my $csv2 = "Veyron2.txt".IO;

    my $hdr1 = "Ключ,Значение,Подтверждено";
    my $hdr2 = "Ключ,Значение,Неудача";

    #ХРАНЕНИЕ ХЭШЕЙ (Ниже, будьте осторожны с пропуском заголовка в файл без заголовка!):

    my %csv1; for $csv1.lines.skip.map( *.split(",").map( *.trim)) {
                  %csv1.push: .[0] => .[1]
              };
    my %csv2; for $csv2.lines.skip.map( *.split(",").map( *.trim)) {
                  %csv2.push: .[0] => .[1]
              };

    #ПРОВЕРКИ:

    die "несколько значений для ключа в файле_1" if any(%csv1.values.map: *.elems > 1).so; 
    die "несколько значений для ключа в файле_2" if any(%csv2.values.map: *.elems > 1).so;

    #ПОДГОТОВКА ВЫХОДНОГО ФАЙЛА С ЗАГОЛОВКОМ:

    !("Veyron_output_verified.txt".IO.e) && (my $fh1 = "Veyron_output_verified.txt".IO.open: :a); 
    !("Veyron_output_failed.txt".IO.e) && (my $fh2 = "Veyron_output_failed.txt".IO.open: :a); 

    $fh1.put: $hdr1;
    $fh2.put: $hdr2;

    #ЦИКЛ ВЫВОДА:

    for %csv1.keys.sort -> $id {
       if  %csv2{$id}:exists {
           if  %csv1{$id}  eq  %csv2{$id} {
               $fh1.put: ($id, %csv1{$id}, "подтверждено").join: ",";
           }
           else {
               $fh2.put: ($id, %csv1{$id}, "несоответствие").join: ",";
           }
       }
       else {
          $fh2.put: ($id, %csv1{$id}, "отсутствует").join: ",";
       }
    }

    $fh1.close;
    $fh2.close;

Вот скрипт, написанный на Raku, языке программирования из семейства Perl. Как и Perl, Raku имеет структуры данных ключ/значение, а также различные операторы для работы с файлами, что делает его идеальным для решения данной задачи. Кратко:

  • В начале $-сигнальные $csv1 и $csv2 обозначают дескрипторы файлов (на самом деле объекты .IO). Поскольку сигналы остаются неизменными в Raku…
  • Два %-сигнальные хэша %csv1 и %csv2 обозначают место хранения ключей/значений, полученных из каждого файла, соответственно.
  • Очистка пробелов, манипуляции с заголовками и проверки обеспечивают удобство для выполнения кода.
  • Операторы файлов, такие как .e для “существует”, гарантируют, что ранее существующий выходной файл не будет перезаписан. Дескрипторы файлов открываются с параметром :a, означающим :прибавить.
  • В цикле вывода ключи из %csv1 ищутся в %csv2, чтобы проверить строковое равенство (с помощью eq) соответствующих значений. Возвращается одна из трех строк: “подтверждено”, “несоответствие” или “отсутствует”.
  • Выходные данные добавляются по строкам к открытым выходным дескрипторам файлов, которые закрываются в конце скрипта.

Примечание: если вы хотите проверить числовое равенство значений для ключей, замените:

%csv1{$id} eq %csv2{$id} на: %csv1{$id} == %csv2{$id}


Пример выхода (“подтверждено”):

Ключ,Значение,Подтверждено
abc,123,подтверждено
def,234,подтверждено

Пример выхода (“неудача”):

Ключ,Значение,Неудача
adf,567,несоответствие

Справка по Raku:
https://docs.raku.org
https://raku.org

Связанный скрипт на Perl:
https://www.perlmonks.org/?node_id=805106

Ответ или решение

Сравнение CSV файлов по двум столбцам: пошаговое руководство

Сравнение CSV файлов — это задачка, с которой могут столкнуться многие специалисты в области IT. В данной статье мы рассмотрим шаги, необходимые для сравнения двух CSV файлов таким образом, чтобы мы могли проверить значения двух столбцов для совпадения. Мы охватим детали, начиная от сортировки файлов до создания выходных файлов с результатами.

Шаг 1: Подготовка данных

Перед тем как начать, давайте определим формат входных файлов. В данном случае у нас есть два CSV файла:

Файл 1:

Column 1, Column 2
abc, 123
def, 234
adf, 567

Файл 2:

Column 1, Column 2
abc, 123
def, 234
adf, 578

Обратите внимание, что каждый файл имеет заголовок и два столбца. Мы будем использовать первый столбец в качестве ключа для сравнения значений во втором столбце.

Шаг 2: Сортировка файлов

Прежде всего, необходимо отсортировать оба файла по первому столбцу. Это можно сделать с помощью команды sort, как показано ниже:

sort file1.csv > file1_sorted.csv
sort file2.csv > file2_sorted.csv

Шаг 3: Объединение и очистка данных

Следующий шаг — объединение этих отсортированных файлов и очистка от ненужных символов. Мы можем использовать команду paste для объединения файлом и sed для удаления запятых:

paste -d ' ' file1_sorted.csv file2_sorted.csv | sed 's/,//g' > combined.csv

Здесь мы создаём файл combined.csv, который будет содержать данные из обоих файлов.

Шаг 4: Сравнение столбцов

На этом этапе мы будем использовать утилиту awk, которая отлично подходит для обработки текстовых данных. Приведем следующую команду, которая будет сравнивать значения во втором столбце для совпадения:

awk '
    BEGIN { FS = OFS = " " }
    NR==1 { print "Column 1", "Column 2", "Status"; next } 
    {
        if ($1 in data) {
            if ($2 == data[$1]) {
                print $1, $2, "Validated" > "verified.csv"
            } else {
                print $1, $2, "Failed" > "failed.csv"
            }
            delete data[$1]
        } else {
            print $1, $2, "Absent" > "failed.csv"
        }
    }
    { data[$1] = $2 }
' combined.csv

Объяснение команды:

  • BEGIN: устанавливает разделитель полей.
  • NR==1: выводит заголовок для результирующего файла.
  • data: ассоциативный массив, который используется для хранения значений столбца 2 из первого файла.
  • Сравнение значений и вывод в соответствующие файлы осуществляется внутри блока if.

Шаг 5: Сохранение результатов

Результаты будут записаны в два отдельных файла:

  • verified.csv — содержит строки, где значения совпадают.
  • failed.csv — содержит строки с несовпадениями или отсутствием ключей.

Заключение

Сравнение CSV файлов может показаться сложной задачей, но с использованием инструментов командной строки, таких как sort, paste, sed и awk, это можно сделать эффективно. Следуя описанным шагам, вы сможете создать простое и быстрое решение для вашей операции по сравнению данных. Не забудьте адаптировать команды и скрипты под ваши текущие нужды и форматы файлов.

Оцените материал
Добавить комментарий

Капча загружается...