awk как удалить дубликаты в полях, только если предыдущие поля совпадают

Я пытаюсь удалить дубликаты из полей (и заменить их пробелами), только если предыдущие поля совпадают. Например:

Пример ввода:

France  Paris      Museum of Fine Arts          blabala
France  Paris      Museum of Fine Arts          blajlk
France  Paris      Yet another museum           lqmsjdf
France  Paris      Museum of National History            mlqskjf
France  Bordeaux   Museum of Fine Arts          qsfsqf
France  Bordeaux   City Hall                lmqjflqsk
France  Bordeaux   City Hall                    lqkjfqlskjflqskfj
Spain   Madrid     Museum of Fine Arts          lqksjfh
Spain   Madrid     Museum of Fine Arts          qlmfjlqsjf
Spain   Barcelona  City Hall                nvqjvvnqk
Spain   Barcelona  Museum of Fine Arts          lmkqjflqksfj

Желаемый результат:

France    Paris        Museum of FineArts                    blabala
                                                             blajlk
                       Yet another museum                    lqmsjdf
                       Museum of National History            mlqskjf
          Bordeaux     Museum of Fine Arts                   qsfsqf
                       City Hall                             lmqjflqsk
                                                             lqkjfqlskjflqskfj
Spain     Madrid       Museum of Fine Arts                   lqksjfh
                                                             qlmfjlqsjf
          Barcelona   City Hall                              nvqjvvnqk
                      Museum of Fine Arts                    lmkqjflqksfj

Заранее большое спасибо за любую помощь.


person Trying    schedule 24.01.2011    source источник
comment
Вы могли бы сделать это в awk с помощью функций, но зачем? Кажется, он лучше подходит для языка более общего назначения. Возможно, Python использует модуль csv? Кроме того, если это не последний шаг, это, вероятно, плохая идея, потому что в будущем будет сложнее анализировать файл.   -  person Hank Gay    schedule 24.01.2011
comment
У меня есть скрипт, который делает здесь почти то же самое: are-the-same" title="awk как удалить дубликаты в полях, только если предыдущие поля одинаковы"> stackoverflow.com/questions/4785566/, но я должен сохранить имена учреждений, которые возвращаются часто. Поэтому на их выделение требуется время (1500 строк). Что касается вывода, он служит моей цели, потому что файл обрабатывается как LaTeX где-то в будущем.   -  person Trying    schedule 24.01.2011
comment
Это ссылка на текущую страницу. Возможно, вы имели в виду этот вопрос< /а>.   -  person Dennis Williamson    schedule 24.01.2011
comment
Записи в порядке? В частности, появится ли France Paris Yet... когда-нибудь между двумя музеями France Paris...? Кроме того, разделены ли поля табуляцией (без пробелов, кроме как внутри полей, и без табуляции внутри полей)?   -  person Dennis Williamson    schedule 24.01.2011
comment
@ Деннис: да, я имел в виду этот сценарий! Именно так я выполнял свою работу до тех пор, пока не понял, что могу добиться большего (хм... кто-то может добиться большего). Я тогда еще не до конца проанализировал проблему (на самом деле был завален). И да, данные сортируются.   -  person Trying    schedule 24.01.2011


Ответы (2)


Попробуйте это:

awk -F '\t' 'BEGIN {OFS=FS} {if ($1 == prev1) $1 = ""; else prev1 = $1; if ($2 == prev2) $2 = ""; else prev2 = $2; if ($3 == prev3) $3 = ""; else prev3 = $3; print}' inputfile

Вот более короткая версия, которая работает для любого количества полей (последнее поле всегда печатается):

awk -F '\t' 'BEGIN {OFS=FS} {for (i=1; i<=NF-1;i++) if ($i == prev[i]) $i = ""; else prev[i] = $i; print}' inputfile

Вывод не будет выровнен для использования на экране, но будет правильное количество вкладок.

Вывод будет выглядеть следующим образом:

field1 TAB field2 TAB field3 TAB field4
TAB TAB TAB field4
TAB TAB field3 TAB field4
TAB field2 TAB field3 TAB field4
etc.

Если вам нужно выровнять столбцы, это также возможно.

Изменить:

Эта версия позволяет указать поля для дедупликации:

#!/usr/bin/awk -f
BEGIN {
    FS="\t"; OFS=FS
    deduplist=ARGV[1]
    ARGV[1]=""
    split(deduplist,tmp," ")
    for (i in tmp) dedup[tmp[i]]=1
}
{
    for (i=1; i<=NF;i++)
        if (i in dedup) {
            if ($i == prev[i])
                $i = ""
            else
                prev[i] = $i
        }
    # prevent printing lines that are completely blank because 
    # it's an exact duplicate of the preceding line and all fields 
    # are being deduplicated
    if ($0 !~ /^[[:blank:]]*$/) 
        print
}

Запустите его следующим образом: ./script.awk "2 3" inputfile для дедупликации полей 2 и 3.

person Dennis Williamson    schedule 24.01.2011
comment
первый скрипт вообще не работает. Второй вариант отлично работает, за исключением того, что он вызовет проблемы в других полях, поскольку они, вероятно, будут содержать повторяющиеся данные, которые я хочу сохранить (например, письмо, отчет и т. д.). Другими словами, я предпочел бы иметь возможность контролировать, в каких полях я удаляю дубликаты (но ты все еще мой гений!). - person Trying; 24.01.2011
comment
@Trying: Это странно, потому что они делают одно и то же. См. мою новую версию, чтобы узнать, как выбрать поля для удаления дубликатов. - person Dennis Williamson; 24.01.2011
comment
Это прекрасно работает. Какой отличный сценарий! Он очень гибкий. Это может сделать меня женщиной, которая нужна каждому на (небольшом) рабочем месте (все еще очень низкая оплата). Поскольку я не знаю, как вас отблагодарить (кроме как отметить ответ как принятый), вот вам моя маленькая благодарность: i.imgur.com/G7UE2.jpg Мой супервайзер отличный. Я уточню, что если она получит свой проклятый PDF-файл так, как хочет, то это благодаря Деннису Уильямсону, и она может только поблагодарить меня за сверхурочную работу (но ведь я опаздываю! -- Эй, не могу же я). есть и то, и другое!) - person Trying; 25.01.2011

Попробуйте этот однострочный Perl:

perl  -F"\t" -nae '@O=@F;if(!$x){$x=1}else{for($i=0;$i<=$#S;$i++){$F[$i]=""if($S[$i] eq "" || $S[$i] eq $F[$i])}};print join "\t",@F;@S=@O;'

Посмотреть

Я предположил, что поля разделены табуляцией.

person codaddict    schedule 24.01.2011
comment
скрипт у меня не работает. Он просто делает отступ для первого поля, когда оно встречается во втором случае. И это первый раз, когда я использую perl... - person Trying; 24.01.2011
comment
@Trying: ввод разделен табуляцией? Я дал рабочую ссылку на ideone, пожалуйста, посмотрите. - person codaddict; 24.01.2011