डुप्लीकेट लाइनें हटाएं जोड़ीदार?


16

मुझे आज इस उपयोग के मामले का सामना करना पड़ा। यह पहली नज़र में सरल लगता है, लेकिन साथ लगभग नगण्य sort, uniq, sedऔर awkबताता है कि यह nontrivial है।

मैं डुप्लिकेट लाइनों के सभी जोड़े कैसे हटा सकता हूं ? दूसरे शब्दों में, यदि किसी दी गई पंक्ति के डुप्लिकेट की संख्या समान है, तो उन सभी को हटा दें; यदि डुप्लिकेट लाइनों की एक विषम संख्या है, तो सभी को हटा दें, लेकिन एक। (सॉर्ट किए गए इनपुट को ग्रहण किया जा सकता है।)

एक स्वच्छ सुरुचिपूर्ण समाधान बेहतर है।

उदाहरण इनपुट:

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

उदाहरण आउटपुट:

a
d
e

जवाबों:


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

या लाइनों के quads निकालने के लिए:

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

sed अधिकांश अन्य विकल्पों पर एक अतिरिक्त लाभ है, जो एक स्ट्रीम में सही मायने में संचालित करने की क्षमता है, डुप्लिकेट के लिए जाँच की जाने वाली लाइनों की वास्तविक संख्या से अधिक मेमोरी स्टोरेज की आवश्यकता नहीं है।


जैसा कि कॉउन्ग्लम ने टिप्पणियों में बताया , सी को लोकेल सेट करना मल्टी-बाइट वर्णों को ठीक से हटाने के लिए विफलताओं से बचने के लिए आवश्यक है। तो ऊपर दिए गए आदेश बन जाते हैं:

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: आप लोकल को सेट करना चाहते हैं C, अन्यथा मल्टी-बाइट लोकेल में, उस लोकेल में अमान्य वर्ण कमांड विफल हो सकता है।
congonglm

4

यह बहुत सुरुचिपूर्ण नहीं है, लेकिन यह उतना ही सरल है जितना कि मैं इसके साथ आ सकता हूं:

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

मूल () uniqआउटपुट को बंद कर देता है। यह तब तक काम करेगा जब तक आपके पास एक पंक्ति के 9,999,999 से अधिक डुप्लिकेट नहीं होंगे (जिस स्थिति में यूनीक का आउटपुट 9 से अधिक अक्षर हो सकता है)।


मैंने कोशिश की uniq -c input | awk '{if ($1 %2 == 1) { print $2 } }'और यह समान रूप से अच्छी तरह से काम करने लगा। किसी भी कारण से substrसंस्करण बेहतर है?
जोसेफ आर।

1
@ जोसेफ।, अगर लाइनों में कोई व्हाट्सएप है तो आपकी टिप्पणी में संस्करण विफल हो जाएगा।
वाइल्डकार्ड

यह सच है। उस स्थिति में, खेतों $2को $NFअधिक मजबूत बनाने के लिए एक लूप नहीं होगा ?
जोसेफ आर।

@ जोसेफ: आप क्यों मानते हैं कि आपका विकल्प अधिक मजबूत होगा? लगातार कई जगह होने पर आपको इसे सही तरीके से काम करने में कठिनाई हो सकती है; जैसे, foo   bar
जी-मैन का कहना है कि 'मोनिका' की बहाली

@ जोसेफ।, नहीं, क्योंकि यह व्हाट्सएप परिसीमन को बदल देगा / समाप्त कर देगा। uniq(कम से कम जीएनयू कोर्यूटिल्स में) पाठ से पहले बिल्कुल 9 वर्णों का मज़बूती से उपयोग करने के लिए लगता है; मुझे यह दस्तावेज कहीं भी नहीं मिला, हालांकि, और यह POSIX चश्मा में नहीं है ।
वाइल्डकार्ड

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

4

साथ pcregrepदिए गए नमूने के लिए:

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

या अधिक सामान्य तरीके से:

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

वहाँ अंत में एक "लाइन के अंत" लंगर नहीं होना चाहिए? अन्यथा आप उस रेखा पर विफल हो जाएंगे जो रेखा से मेल खाती है और उसके पीछे अन्य पात्रों के साथ मेल खाती है।
वाइल्डकार्ड

@Wildcard हाँ, यह बेहतर है। सही किया, thx
जिमीज

बहुत ही शांत! (+1)
जोजो

4

यदि इनपुट सॉर्ट किया गया है:

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

आपके पास यहां एंकरिंग फेल है। उदाहरण के लिए इसे चलाने की कोशिश करें pineapple\napple\ncoconutऔर आउटपुट है pinecoconut
वाइल्डकार्ड

@Wildcard: धन्यवाद। तुम सही हो। देखें कि क्या मेरा अपडेट समझ में आता है ...
JJoao

1
हां। मैं सोच रहा था कि आप दिए गए संशोधक के \nबजाय क्यों उपयोग कर रहे थे , लेकिन फिर मुझे एहसास हुआ कि उपयोग करने से हटाए गए लाइनों के स्थान पर एक रिक्त रेखा निकल जाएगी। अब अच्छा लग रहा है; मैंने गलत संस्करण हटा दिया है क्योंकि यह सिर्फ शोर मिला है। :)$/m$
वाइल्डकार्ड

@ गिल्डकार्ड, शोर में कमी के लिए धन्यवाद
J

3

मुझे यह पसंद pythonहै, उदाहरण के लिए 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

जैसा कि मैंने उस प्रश्न को समझा था जिसे मैंने awk के लिए चुना था, प्रत्येक रिकॉर्ड के एक हैश का उपयोग करते हुए, इस मामले में मैं मान रहा हूँ कि RS = \ n, लेकिन इसे किसी अन्य प्रकार की व्यवस्था पर विचार करने के लिए बदला जा सकता है, इसे एक विचार करने के लिए व्यवस्थित किया जा सकता है एक प्रतिनिधि या एक छोटे से संवाद के साथ, विषम के बजाय प्रतिनिधि की संख्या। हर लाइन का उपयोग हैश के रूप में किया जाता है और इसकी गिनती बढ़ाई जाती है, फाइल के अंत में सरणी स्कैन की जाती है और रिकॉर्ड के हर गिनती को प्रिंट करता है। मैं जाँच करने के लिए गिनती में शामिल हूँ, लेकिन एक [x] को निकालना उस समस्या को हल करने के लिए पर्याप्त है।

HTH

काउंटलाइन कोड

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

यह awkकोड का एक अच्छा टुकड़ा है , लेकिन दुर्भाग्य से awkसाहचर्य सरणियों का आदेश नहीं दिया जाता है, और न ही वे आदेश-संरक्षण कर रहे हैं।
वाइल्डकार्ड

@Wildcard, मैं आपसे सहमत हूं, अगर आपको किसी तरह के ऑर्डर के बजाय इनपुट ऑर्डर की आवश्यकता होती है, तो इसे एक अतिरिक्त हैश की के माध्यम से लागू किया जा सकता है, इसका लाभ यह है कि आपको इनपुट को क्रमबद्ध करना होगा, क्योंकि क्रम से एक छोटे से आउटपुट के साथ अंत में बनाया जा सकता है;)
मोइज़ नजार

अगर आपको संरक्षित किए जाने के आदेश की आवश्यकता है, तो कृपया इस प्रश्न में उल्लेख करें। यह दृष्टिकोण भी मेरा पहला विचार था और आपने यह कहने के अलावा अन्य आदेश का उल्लेख नहीं किया कि हम मान सकते हैं कि फ़ाइल क्रमबद्ध है। बेशक, अगर फ़ाइल सॉर्ट की जाती है, तो आप हमेशा इस समाधान के आउटपुट को पास कर सकते हैं sort
terdon

@terdon, बेशक आप सही हैं; आउटपुट को फिर से हल किया जा सकता है। अच्छी बात। यह भी ध्यान देने योग्य है कि !=0यह अनुमान लगाया गया है कि कैसे awkसंख्याओं को सही / गलत मानों में परिवर्तित किया जाता है , जिससे यह पुनर्निर्देशित हो जाता हैawk '{a[$0]++}END{for(x in a)if(a[x]%2)print x}'
Wildcard

1

यदि इनपुट को इसके बारे में क्रमबद्ध किया जाता है 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

पर्ल के साथ:

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

1

शेल निर्माण का उपयोग करना,

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

1
व्हॉट्सएप (या अधिक, क्योंकि आप उद्धरण करना भूल गए $b) के साथ शुरू या समाप्त होने वाली लाइनों के साथ टूट जाता है ।
गिल्स एसओ- बुराई को रोकना '

1

मजेदार पहेली!

पर्ल में:

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

हास्केल में शब्दशः

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

0

एक संस्करण: मैं आंतरिक लूप को सरल बनाने के लिए "सीमांकक" का उपयोग करता हूं (यह मानता है कि पहली पंक्ति नहीं है __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 के एक से अधिक नहीं है, तो हम याद किए गए पैटर्न की एक घटना को प्रिंट करते हैं
    • और हर मामले में जब पैटर्न बदल गया है: नया याद किया गया पैटर्न वर्तमान पैटर्न है, और हमने इसे केवल एक बार देखा था।
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.