Удалять повторяющиеся строки попарно?

Сегодня я столкнулся с этим прецедентом. Это кажется простым на первый взгляд, но возиться с помощью sort, uniq, sed и awk показал, что это нетривиально.

Как я могу удалить все пары дублированных строк? Другими словами, если существует четное количество дубликатов данной строки, удалите все из них; если есть нечетное количество повторяющихся строк, удалите все, кроме одного. (Можно выбрать отсортированный вход.)

Предпочтительным является чистое элегантное решение.

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

a
a
a
b
b
c
c
c
c
d
d
d
d
d
e

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

a
d
e
16 голосов | спросил Wildcard 18 PMpMon, 18 Apr 2016 23:50:19 +030050Monday 2016, 23:50:19

10 ответов


6

Я разработал ответ sed вскоре после того, как я разместил этот вопрос; никто еще не использовал sed, так что вот оно:

sed '$!N;/^\(.*\)\n\1$/d;P;D'

Маленькая игра с более общей проблемой (как насчет удаления строк в наборах из трех? Или четыре или пять?) при условии следующего расширяемого решения:

sed -e ':top' -e '$!{/\n/!{N;b top' -e '};};/^\(.*\)\n\1$/d;P;D' temp

Для удаления троек линий:

sed -e ':top' -e '$!{/\n.*\n/!{N;b top' -e '};};/^\(.*\)\n\1\n\1$/d;P;D' temp

Или удалить квадратики строк:

sed -e ':top' -e '$!{/\n.*\n.*\n/!{N;b top' -e '};};/^\(.*\)\n\1\n\1\n\1$/d;P;D' temp

sed имеет дополнительное преимущество по сравнению с большинством других опций, то есть его способность действительно работать в потоке, без хранения памяти необходимо, чем фактическое количество строк для проверки дубликатов.


Как cuonglm указал в комментариях , установка языкового стандарта на C необходима, чтобы избежать сбоев в правильном удалении строк, содержащих многобайтные символы. Таким образом, команды выше:

LC_ALL=C sed '$!N;/^\(.*\)\n\1$/d;P;D' temp
LC_ALL=C sed -e ':top' -e '$!{/\n/!{N;b top' -e '};};/^\(.*\)\n\1$/d;P;D' temp
LC_ALL=C sed -e ':top' -e '$!{/\n.*\n/!{N;b top' -e '};};/^\(.*\)\n\1\n\1$/d;P;D' temp
# Etc.
ответил Wildcard 19 AMpTue, 19 Apr 2016 00:14:55 +030014Tuesday 2016, 00:14:55
4

Это не очень элегантно, но это так просто, как я могу придумать:

uniq -c input | awk '{if ($1 % 2 == 1) { print substr($0, 9) }}'

Подстрока () просто отключает вывод uniq. Это будет работать до тех пор, пока у вас не будет более 9999999 дубликатов строки (в этом случае вывод uniq может разлиться более 9 символов).

ответил Jeff Schaller 19 AMpTue, 19 Apr 2016 00:00:24 +030000Tuesday 2016, 00:00:24
4

Попробуйте этот скрипт awk:

#!/usr/bin/awk -f
{
  if ((NR!=1) && (previous!=$0) && (count%2==1)) {
    print previous;
    count=0;
  }
  previous=$0;
  count++;
}
END {
  if (count%2==1) {
    print previous;
  }
}

Предполагается, что файл lines.txt сортируется.

Тест:

$ chmod +x script.awk
$ ./script.awk lines.txt
a
d
e
ответил Jay jargot 19 AMpTue, 19 Apr 2016 00:10:03 +030010Tuesday 2016, 00:10:03
4

С помощью pcregrep для данного образца:

pcregrep -Mv '(.)\n\1$' file

или более общим способом:

pcregrep -Mv '(^.*)\n\1$' file
ответил jimmij 19 AMpTue, 19 Apr 2016 00:41:29 +030041Tuesday 2016, 00:41:29
4

Если вход сортируется:

perl -0pe  'while(s/^(.*)\n\1\n//m){}'
ответил JJoao 19 AMpTue, 19 Apr 2016 02:10:08 +030010Tuesday 2016, 02:10:08
3

Мне нравится python для этого, например, с помощью python 2.7 +

from itertools import groupby
with open('input') as f:
    for k, g in groupby(f):
            if len(list(g)) % 2:
                    print(k),
ответил iruvar 19 AMpTue, 19 Apr 2016 03:47:55 +030047Tuesday 2016, 03:47:55
2

Как я понял вопрос, который я выбрал для awk, используя хэш каждой записи, в этом случае я предполагаю, что RS = \ n, но его можно изменить, чтобы рассмотреть любые другие виды аранжировок, он может быть организован рассмотреть четное количество повторений, вместо нечетного, с параметром или небольшим диалогом. Каждая строка используется как хэш, и ее счет увеличивается, в конце файла массив сканируется и печатает каждый четный счет записи. Я включаю счет, чтобы проверить, но удалить [x] достаточно, чтобы решить эту проблему.

НТН

countlines code

#!/usr/bin/nawk -f
{a[$0]++}
END{for (x in a) if (a[x]%2!=0) print x,a[x] }

Пример данных:

a
One Sunny Day
a
a
b
my best friend
my best friend
b
c
c
c
One Sunny Day
c
d
my best friend
my best friend
d
d
d
One Sunny Day
d
e
x
k
j
my best friend
my best friend

Пример прогона:

countlines feed.txt
j 1
k 1
x 1
a 3
One Sunny Day 3
d 5
e 1
ответил Moises Najar 19 AMpTue, 19 Apr 2016 05:50:08 +030050Tuesday 2016, 05:50:08
1

Используя конструкции оболочки,

uniq -c file | while read a b; do if (( $a & 1 == 1 )); then echo $b; fi done
ответил Guido 19 AMpTue, 19 Apr 2016 00:03:51 +030003Tuesday 2016, 00:03:51
1

Забавная головоломка!

В Perl:

#! /usr/bin/env perl

use strict;
use warnings;

my $prev;
while (<>) {
  $prev = $_, next unless defined $prev;  # prime the pump

  if ($prev ne $_) {
    print $prev;
    $prev = $_;                           # first half of a new pair
  }
  else {
    undef $prev;                          # discard and unprime the pump
  }
}

print $prev if defined $prev;             # possible trailing odd line

Вербально в Haskell:

main :: IO ()
main = interact removePairs
  where removePairs = unlines . go . lines
        go [] = []
        go [a] = [a]
        go (a:b:rest)
          | a == b = go rest
          | otherwise = a : go (b:rest)

Терси в Хаскелле:

import Data.List (group)
main = interact $ unlines . map head . filter (odd . length) . group . lines
ответил Greg Bacon 20 AMpWed, 20 Apr 2016 00:04:45 +030004Wednesday 2016, 00:04:45
0

a version: я использую «разделители» для упрощения внутреннего цикла (он предполагает, что первая строка не является __unlikely_beginning__), и она предполагает, что текст не заканчивается строкой: __unlikely_ending__, и добавьте эту специальную строку разделителя в конце введенных строк. Таким образом, алгоритм может принимать оба:)

{ cat INPUTFILE_or_just_-  ; echo "__unlikely_ending__" ; } | awk '
  BEGIN {mem="__unlikely_beginning__"; occured=0; }  

    ($0 == mem)            { occured++ ; next } 

    ( occured%2 )           { print mem ;} 
                            { mem=$0; occured=1; }
'

Итак:

  • мы помним шаблон, который мы сейчас рассматриваем, увеличивая его на единицу каждый раз, когда он повторяется. [и если это повторилось, мы пропустим следующие 2 действия, которые относятся к случаю, когда шаблон изменяется]
  • Когда шаблон ИЗМЕНЯЕТСЯ:
    • если не кратно 2, мы печатаем одно появление запомненного шаблона
    • и в каждом случае, когда шаблон изменился: новый запомненный шаблон является текущим шаблоном, и мы видели его только один раз.
ответил Olivier Dulac 19 PMpTue, 19 Apr 2016 18:09:24 +030009Tuesday 2016, 18:09:24

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

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132