как удалить некоторые строки в первом файле на основе информации из второго файла?

Решение в TXR Lisp:

txr -e  '(each ((line (get-lines)))
           (set [line (rest (where (op eql #\,) line))]
                (repeat ";"))
           (put-line line))' < input

"Для каждой строки установите остальные места (т.е. все, кроме первого), где эта строка равна запятой, на точку с запятой. Выведите строку. "

Аннотированная расшифровка фоновой интерактивной сессии слушателя:

$ txr
This is the TXR Lisp interactive listener of TXR 148.
Use the :quit command or type Ctrl-D on empty line to exit.

Бесконечный ленивый список точек с запятой:

1> (take 3 (repeat ";"))
(#\; #\; #\;)

Связывание переменных:

2> (let ((a "a,b,c"))
     a)
"a,b,c"

Скобочная нотация извлекает индексы:

3> (let ((a "a,b,c"))
     [a '(1 3)])
",,"

функция where извлекает позиции, в которых предикат истинен:

4> (let ((a "a,b,c"))
     (where (op eql #\,) a))
(1 3)

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

5> (let ((a "a,b,c"))
     (set [a '(1 3)] (repeat ";"))
     a)
"a;b;c"

Динамически вычисляем индексы с помощью where:

6> (let ((a "a,b,c"))
     (set [a (where (op eql #\,) a)] (repeat ";"))
     a)
"a;b;c"

Вырезаем первый индекс с помощью rest, чтобы не сбивать первую запятую точкой с запятой:

7> (let ((a "a,b,c"))
     (set [a (rest (where (op eql #\,) a))] (repeat ";"))
     a)
"a,b;c"

Тестируем с другими строками. Упс, """ не модифицируется:

8> (let ((a ""))
     (set [a (rest (where (op eql #\,) a))] (repeat ";"))
     a)
** replace-str: "" of type lit is not a modifiable string
** during evaluation of form (sys:dwim-set a #:g0145 #:g0144)
** ... an expansion at expr-8:2 of (sys:dwim-set (#:g0146) #:g0145
                                                 #:g0144)
** which is located at expr-8:2

Попробуйте еще раз:

9> (let ((a (copy "")))
     (set [a (rest (where (op eql #\,) a))] (repeat ";"))
     a)
""

Строка без запятых:

10> (let ((a "a"))
     (set [a (rest (where (op eql #\,) a))] (repeat ";"))
     a)
"a"

Строка с одной запятой: замены не происходит.

11> (let ((a "a,b"))
     (set [a (rest (where (op eql #\,) a))] (repeat ";"))
     a)
"a,b"

2
06.10.2016, 15:16
2 ответа

Если perl решение в порядке. Предполагается, что данные столбца 1 и столбца 2 в файле 1 упорядочены таким образом, что значение в столбце 1 всегда меньше, чем столбец 2

$ perl -lane '
    if(!$nf)
    {
        if($#F > 0)
        {
            foreach $i (0..$#F)
            {
                $h{"-$F[$i]-$F[$_]-"}++ foreach ($i+1..$#F)
            }
        }
        $nf++ if eof;
    }
    else
    {
        print if $h{"-$F[0]-$F[1]-"}
    }
' file2 file1
    5   6       0.995
    5   7       0.658
    6   7       0.431
  • Сначала хэш ключей создается в файле 2 парами (опять же при условии, что числа расположены в порядке возрастания) { {1}}
    • значения двух столбцов окружены - , чтобы предотвратить возможное несоответствие, например 11 и 20 vs 1 и 120
  • Затем для строк файла1 выведите строку, если ключ существует


Если файл2 изменен на

$ cat file2
1
3 4
5 6 7 8

$ perl -lane '
    if(!$nf)
    {
        if($#F > 0)
        {
            foreach $i (0..$#F)
            {
                $h{"-$F[$i]-$F[$_]-"}++ foreach ($i+1..$#F)
            }
        }
        $nf++ if eof;
    }
    else
    {
        print if $h{"-$F[0]-$F[1]-"}
    }
' file2 file1
    3   4       0.995
    5   6       0.995
    5   7       0.658
    5   8       0.000
    6   7       0.431
    6   8       0.000
    7   8       0.000
3
27.01.2020, 21:54

Использование awk.

Первая команда awk создает файл со всеми парами. Вторая команда awk считывает файл пар один раз в массив, а затем печатает все совпадающие строки.

awk 'NF>1{for(i=1;i<=NF;i++) for(j=i+1;j<=NF;j++) print $i,$j;}' file2 > /tmp/pairs
awk 'BEGIN{while((getline <"/tmp/pairs") > 0) pair[$1,$2]=1; close("/tmp/pairs")} ($1,$2) in pair' file1

Второй команде может потребоваться много памяти! Если файлы упорядочены, мы могли бы каким-то образом избежать массива, читая оба файла одновременно.Примечание. Я использую две команды, чтобы просмотреть файл пар перед окончательным запуском.

Здесь тот же код, что и для одной команды, в удобном для чтения формате:

awk '
  BEGIN {
    f="file2"
    while((getline <f) > 0)
      for(i=1;i<=NF;i++)
        for(j=i+1;j<=NF;j++)
          pair[$i,$j]=1;
    close(f);
  }
  ($1,$2) in pair
' file1

FYI Я провел несколько тестов для файлов file1 (22 миллиона строк), file2 (4 миллиона строк), что дает 2 миллиона строк. линии вывода.

  • gawk: 9,6 секунды, 275 МБ памяти
  • mawk: 20,7 секунды, 134 МБ памяти
  • Perl-ответ Sundeep: 31,9 секунды, 231 МБ памяти
3
27.01.2020, 21:54

Теги

Похожие вопросы