Решение в 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"
Если 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
-
, чтобы предотвратить возможное несоответствие, например 11
и 20
vs 1
и 120
Если файл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
Использование 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 миллиона строк. линии вывода.