Yinelenen satırlar çift olarak silinsin mi?


16

Bugün bu kullanım örneğiyle karşılaştım. İlk bakışta basit gibi görünüyor, ama ile ilgilenmek sort, uniq, sedve awkbu aşikar olmayan var olduğunu ortaya koydu.

Tüm yinelenen satır çiftlerini nasıl silebilirim ? Başka bir deyişle, belirli bir satırın çift sayıda kopyası varsa, tümünü silin; tek sayıda yinelenen satır varsa, biri hariç tümünü silin. (Sıralı giriş olduğu varsayılabilir.)

Temiz ve zarif bir çözüm tercih edilir.

Örnek girdi:

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

Örnek çıktı:

a
d
e

Yanıtlar:


6

Ben çalıştım sedben bu soruyu yayınlanmıştır sonra uzun değil cevabı; sedŞimdiye kadar hiç kimse şu ana kadar kullanmadı:

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

Daha genel bir problemle biraz oynamak (üç veya dört veya beşli setlerde satırları silmek ne olacak?) Aşağıdaki genişletilebilir çözümü sağladı:

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

Üçlü çizgileri kaldırmak için uzatıldı:

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

Veya dörtlü satırları kaldırmak için:

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

sed diğer birçok seçeneğe göre ek bir avantaja sahiptir, bu da gerçek zamanlı olarak çalışabilme yeteneğidir, kopyalar için kontrol edilecek gerçek hat sayısından daha fazla bellek depolamasına gerek yoktur.


As cuonglm Açıklamalarda belirttiği düzgün multi-byte karakterleri içeren satırları kaldırmak için C yerel önlemek arızaları için gereklidir ayarı. Böylece yukarıdaki komutlar:

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.

2
@Wildcard: Yerel ayarı C, aksi halde çok baytlı yerel ayarlarda, bu yerel ayardaki geçersiz karaktere komutun başarısız olmasına neden olacak şekilde ayarlamak isteyebilirsiniz .
cuonglm

4

Çok zarif değil, ama gelebildiğim kadar basit:

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

Substr () sadece uniqçıktıyı keser . Bu, bir satırın 9,999,999'dan fazla kopyası olana kadar işe yarayacaktır (bu durumda uniq'in çıktısı 9 karakterden fazla dökülebilir).


Denedim uniq -c input | awk '{if ($1 %2 == 1) { print $2 } }'ve eşit derecede iyi çalışıyor gibi görünüyordu. substrSürümün daha iyi olmasının herhangi bir nedeni var mı?
Joseph

1
@JosephR., Satırlarda boşluk varsa yorumunuzdaki sürüm başarısız olur.
Wildcard

Bu doğru. Bu durumda, bir döngü alanları yazdırmak için olmaz $2için $NFdaha sağlam olacağı?
Joseph R.

@JosephR .: Alternatifinizin neden daha sağlam olacağına inanıyorsunuz? Birbirini izleyen birden çok boşluk olduğunda düzgün çalışmasını sağlamakta zorluk çekebilirsiniz; ör foo   bar.
G-Man, 'Monica'yı Yeniden Başlat' diyor

@JosephR., Hayır, çünkü boşluk sınırlamasını değiştirir / ortadan kaldırır. uniq(en azından GNU coreutils'te) metnin kendisinden önce tam olarak 9 karakter kullanıyor gibi görünüyor; Ancak bu belgeyi hiçbir yerde belgelendiremiyorum ve POSIX teknik özelliklerinde değil .
Wildcard,

4

awkAşağıdaki bu komut dosyasını deneyin :

#!/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;
  }
}

Var olduğu varsayılmaktadır. lines.txtDosyanın sıralandığı .

Test:

$ chmod +x script.awk
$ ./script.awk lines.txt
a
d
e

4

İle pcregrepverilen bir örnek için:

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

veya daha genel bir şekilde:

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

Sonunda bir "hat sonu" ankrajı olmamalı mı? Aksi takdirde, sondaki karakterlere sahip olmaktan önceki satırla eşleşen bir satırda başarısız olursunuz.
Wildcard

@Wildcard evet, bu daha iyi. düzeltilmiş, thx.
jimmij

Çok havalı! (+1)
JJoao

4

Giriş sıralanırsa:

perl -0pe  'while(s/^(.*)\n\1\n//m){}'

Burada bir demirleme hatası var. Örneğin çalıştırmayı deneyin pineapple\napple\ncoconutve çıkış pinecoconut.
Joker

@Wildcard: teşekkür ederim. Haklısın. Güncellememin mantıklı olup olmadığına bakın ...
JJoao

1
Evet. Neden kullandığınız merak \nyerine $verilen /mdeğiştirici, ama sonra fark kullanılarak $silinen numaraları yerine boş bir satır bırakacaktı. Şimdi iyi görünüyor; Gürültü eklediğinden beri yanlış sürümü kaldırdım. :)
Joker

@wildcard, gürültü azaltma için teşekkürler ☺
JJoao

3

Bunu beğendim python, örneğin python2.7+

from itertools import groupby
with open('input') as f:
    for k, g in groupby(f):
            if len(list(g)) % 2:
                    print(k),

2

Her kayıt için bir karma kullanarak awk için tercih ettiğim soruyu anladığım gibi, bu durumda RS = \ n olduğunu varsayıyorum, ancak başka herhangi bir düzenlemeyi düşünmek için değiştirilebilir, bir bir parametre veya küçük bir iletişim kutusu ile tek yerine çift sayısı. Her satır karma olarak kullanılır ve sayısı artar, dosyanın sonunda dizi taranır ve kaydın her çift sayısını yazdırır. Kontrol etmek için sayımı dahil ediyorum, ancak bir sorunu kaldırmak için [x] 'i kaldırmak yeterlidir.

HTH

countlines kodu

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

Örnek veri:

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

Örnek Çalışma:

countlines feed.txt
j 1
k 1
x 1
a 3
One Sunny Day 3
d 5
e 1

Güzel bir awkkod parçası , ama ne yazık ki awkilişkilendirilebilir diziler hiç sipariş edilmiyor, ne de sipariş koruyorlar.
Joker

@Wildcard, size katılıyorum, bir sıralama düzeni yerine giriş sırasına ihtiyacınız varsa, ekstra bir karma anahtarı ile uygulanabilir, bunun avantajı, sıralama düzeninden beri girişi sıralamak zorunda kalmamanızdır. daha küçük bir çıktı ile sonunda yapılabilir;)
Moises Najar

@Wildcard korunacak sipariş gerekiyorsa, lütfen soruda belirtin. Bu yaklaşım benim de ilk düşüncemdi ve dosyanın sıralandığını varsayabileceğimizden başka bir düzenden bahsetmiyorsunuz. Tabii ki, dosya sıralanırsa, bu çözümün çıktısını her zaman iletebilirsiniz sort.
terdon

@terdon, elbette haklısın; çıktı yeniden sıralanabilir. İyi bir nokta. Ayrıca , sayıları doğru / yanlış değerlere !=0nasıl awkdönüştürdüğünü ima ettiğine dikkat çekerek, bunu azaltılabilir awk '{a[$0]++}END{for(x in a)if(a[x]%2)print x}'
Wildcard

1

Girdi sıralanırsa, buna ne dersiniz awk:

awk '{ x[$0]++; if (prev != $0 && x[prev] % 2 == 1) { print prev; } prev = $0; } END { if (x[prev] % 2 == 1) print prev; }' sorted

1

perl ile:

uniq -c file | perl -lne 'if (m(^\s*(\d+) (.*)$)) {print $2 if $1 % 2 == 1}'

1

Kabuk yapılarını kullanma,

uniq -c file | while read a b; do if (( $a & 1 == 1 )); then echo $b; fi done

1
Bu, boşlukla başlayan veya biten satırlarla kesiliyor (veya daha fazlası, çünkü alıntı yapmayı unuttunuz $b).
Gilles 'SO- kötü olmayı bırak

1

Eğlenceli bulmaca!

Perl dilinde:

#! /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'de ayrıntılı olarak:

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)

Tersine Haskell'de:

import Data.List (group)
main = interact $ unlines . map head . filter (odd . length) . group . lines

0

bir sürüm: İç döngüyü basitleştirmek için "ayırıcılar" kullanıyorum (ilk satırın olmadığını __unlikely_beginning__varsayar ve metnin satırla bitmediğini varsayar: __unlikely_ending__ve girilen satırların sonuna bu özel sınırlayıcı satırı ekler. algoritma her ikisini de alabilir:)

{ 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; }
'

Yani :

  • şu anda baktığımız kalıbı hatırlıyoruz ve her tekrarladığında bir tane arttırıyoruz. [ve tekrar ettiyse, desen değiştiğinde geçerli olan sonraki 2 işlemi atlarız]
  • Desen DEĞİŞTİRİLDİĞİNDE:
    • 2'nin katları değilse, ezberlenen kalıbın bir örneğini yazdırırız
    • ve her durumda desen değiştiğinde: yeni belleğe alınan desen geçerli desendir ve sadece bir kez gördük.
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.