
Я все еще учусь программированию и перепробовал много вещей, но так и не могу получить правильный формат. У меня естьс разделителем табуляциифайл с 17 столбцами и множеством (около 50.000) строк. Файл уже отсортирован по первому столбцу. Я хочу объединить строки, которые имеют одинаковый первый столбец (A), но все остальные 16 столбцов отличаются, и я хочу сохранить всю информацию в одной строке, желательно в том же столбце сточка с запятой ;как разделитель между ними. Я хочу сохранить табуляцию как разделитель в выходном файле. Большое спасибо за ответы, и если бы вы также могли объяснить ответ, где я ошибся, это было бы еще лучше :).
Я уже пробовал:
awk -F'\t' 'NF>1{a[$1] = a[$1]";"$2}END{for(i in a){print i""a[i]}}' filename.txt
perl -F',' -anle 'next if /^$/;$h{$F[0]} = $h{$F[0]}.", ".$F[1];
END{print $_,$h{$_},"\n" for sort keys %h}' filename.txt
ФОРМАТ ФАЙЛА (остальные 15 столбцов имеют тот же формат, что и столбец B)
A B C
123 fvv ggg
123 kjf ggg
123 ccd att
567 abc gst
567 abc hgt
879 ttt tyt
Нужный мне вывод (мне нужны все 17 столбцов, а для столбцов 2-16 мне нужен тот же вывод, что и в столбцах B и C). Все случаи B должны быть под B, все случаи C должны быть под C, все случаи D должны быть под D и т. д. Таким образом, вывод содержит 17 столбцов, как и ввод, и вместо 50 000 строк теперь должно быть около 20 000, поскольку для столбца 1 (для этого конкретного файла) есть много повторений:
A B C
123 fvv;kjf;ccd ggg;ggg;att
567 abc;abc gst;hgt
879 ttt lll
решение1
awk '{
if(NR!=1){a[$1]=$2";"a[$1]}
else print $0}
END{
n = asorti(a, b);
for (n in b) {
print b[n],a[b[n]]
}
}'
решение2
Решение на Perl:
$ perl -F"\t" -anle 'if($.==1){print; next} push @{$k{$F[0]}},@F[1..$#F];
END{print "$_\t" . join(";",@{$k{$_}}) for sort keys(%k)}' file
A B
123 fvv;kjf;ccd
567 abc;abc
879 ttt
Это может работать с произвольным количеством полей. Однако это требует загрузки довольно большого количества вещей в память, и это может стать проблемой, если ваш файл большой.
Что касается того, где вы ошиблись, мы не сможем вам сказать, пока вы не объясните, что на самом деле произошло, но, навскидку, ваша попытка с помощью Perl потерпит неудачу, потому что:
- Вы используете
-F,
, который устанавливает разделитель полей на запятую, когда ваши входные данные содержат символы табуляции. - Вы используете
-l
иprint "foo\n"
. Он-l
уже добавляет новую строку к каждому вызову print, поэтому у вас будет несколько пустых строк. - Вы используете
$h{$F[0]}.", ".$F[1];
для добавления, поэтому при первом запуске, когда$h{$F[0]}
он не определен, вы добавите дополнительный символ,
в начало сохраненного значения. - Вы смотрите только на второе поле, игнорируя все остальные.
Аналогично, вы awk
потерпите неудачу, потому что:
- Вы печатаете
foo""bar
, что объединит вывод без пробелов между каждым полем. Вы хотитеprint foo,bar
и вы также хотитеOFS="\t"
для вывода с разделителями табуляции. - Вы смотрите только на второе поле, игнорируя все остальные.
решение3
извините за эту однострочную фразу, но вот она —
awk 'BEGIN{FS="\t"} {for(i=2; i<=NF; i++) { if (!a[$1]) a[$1]=$1FS$i ;else a[$1]=a[$1]";"$i};if ($1 != old) b[j++] = a[old];old=$1 } END{for (i=0; i<j; i++) print b[i] }' 1
123 fvv ;kjf;ccd
567 abc;abc
879 ttt
решение4
awk '
function p(n,A){
s = n
for(i=2;i<=NF;i++){
s = s "\t" A[i]
A[i] = $i
}
if(n)
print s
}
NR==1{
print
next
}
$1==n{
for(i=2;i<=NR;i++)
A[i] = A[i] ";" $i
next
}
{
p(n,A)
n = $1
}
END{
p(n,A)
}
' file