टेक्स्ट फ़ाइल के अंदर डुप्लिकेट लाइनों को कैसे हटाएं?


126

मेरी एक विशाल (2 GiB तक) की टेक्स्ट फाइल में हर लाइन की लगभग 100 डुप्लिकेट डुप्लिकेट हैं (मेरे मामले में बेकार है, क्योंकि फाइल CSV जैसी डेटा टेबल है)।

जबकि (अधिमानतः, लेकिन यह महत्वपूर्ण प्रदर्शन को बढ़ावा देने के लिए बलिदान किया जा सकता है) मूल अनुक्रम क्रम को बनाए रखने के लिए सभी पुनरावृत्तियों को हटाने की आवश्यकता है। परिणाम में प्रत्येक पंक्ति को अद्वितीय होना है। यदि 100 समान लाइनें थीं (आमतौर पर डुप्लिकेट फ़ाइल में फैले हुए हैं और पड़ोसी नहीं होंगे) तो केवल एक ही तरह का शेष होना है।

मैंने इसे लागू करने के लिए स्काला में एक कार्यक्रम लिखा है (इसे जावा समझें अगर आपको स्काला के बारे में जानकारी नहीं है)। लेकिन हो सकता है कि तेजी से सी-लिखित देशी उपकरण इस तेजी से करने में सक्षम हैं?

अद्यतन: awk '!seen[$0]++' filenameसमाधान मेरे लिए ठीक तब तक काम कर रहा था जब तक कि फाइलें 2 GiB या उससे छोटी थीं, लेकिन अब जब मैं 8 GiB फाइल को साफ करने के लिए हूं तो यह किसी भी अधिक काम नहीं करता है। यह 4 GiB रैम और 64-बिट विंडोज 7 PC के साथ 4 गीगा RAM और 6 GiB स्वैप के साथ एक मैक पर अनन्तता ले रहा लगता है। और मैं इस अनुभव को देखते हुए 4 gb RAM के साथ लिनक्स पर इसे आज़माने में उत्साह महसूस नहीं करता।


यह आपके आदेश को नष्ट कर देगा, लेकिन क्या आपने सॉर्ट करने की कोशिश की है

5
सी अक्सर जावा की तुलना में काफी तेज नहीं होता है, और यदि आप इसे (इन-ऑर्डर) अभी चला रहे हैं, तो इससे पहले कि आप यहां कोई उत्तर प्राप्त करें, इसे लागू करें, और इसे पूरा करना समाप्त कर लें, यह एक उचित मौका है। आदेश से बाहर, sort -uशायद तेज हो जाएगा।
केविन

जवाबों:


214

awk# बश (Freenode) पर देखा गया एक समाधान:

awk '!seen[$0]++' filename

1
बस एक 2 जी फ़ाइल पर यह कोशिश की और मेरी नोटबुक पर तीन मिनट लग गए। बुरा नहीं। मैंने भी uniq filename की कोशिश की | awk '! देखा [$ 0] ++', लेकिन यह कोई तेज़ नहीं था।
mgjk

यह आश्चर्यजनक रूप से awk2 सरणी लुकअप (गाइल्स उत्तर में एक विस्तारित विवरण के रूप में दिखाया गया है) का उपयोग करके अधिक वर्बोज़ संस्करण की तुलना में तेज़ है : 0m36.132s बनाम 0m49.958s .. 50 मिलियन लाइनों के लिए .. मुझे लगा कि अड़चन I / O होगी, लेकिन अतिरिक्त सरणी लुकअप है ... सरणी में 1 मिलियन तत्व एक महत्वपूर्ण
दाँत

लेकिन उस तरह की तुलना कैसे करता है -u ....?
हशवॉर्डर

1
@HashWizard: यह कमांड सॉर्ट नहीं करता है, लेकिन एक ही लाइन की हर अगली घटना को खत्म करता है
enzotib

1
@MaxWilliams हाँ, यह काम करता है वे बेतरतीब ढंग से वितरित कर रहे हैं।
सेहोपोलोपस

47

मानक उपयोगिताओं का उपयोग करने के लिए एक सरल (जो स्पष्ट नहीं कहना है) विधि है जिसे चलाने के अलावा एक बड़ी मेमोरी की आवश्यकता नहीं है sort, जिसमें अधिकांश कार्यान्वयन में विशाल फ़ाइलों (एक अच्छा बाहरी सॉर्ट एल्गोरिथ्म) के लिए विशिष्ट अनुकूलन हैं। इस पद्धति का एक फायदा यह है कि यह केवल विशेष-प्रयोजन उपयोगिताओं के अंदर सभी लाइनों पर लूप करता है, कभी भी व्याख्या की गई भाषाओं के अंदर नहीं।

<input nl -b a -s : |           # number the lines
sort -t : -k 2 -u |             # sort and uniquify ignoring the line numbers
sort -t : -k 1n |               # sort according to the line numbers
cut -d : -f 2- >output          # remove the line numbers

यदि सभी लाइनें एक गैर-व्हाट्सएप चरित्र के साथ शुरू होती हैं, तो आप कुछ विकल्पों से दूर हो सकते हैं:

<input nl | sort -k 2 -u | sort -k 1n | cut -f 2- >output

बड़ी मात्रा में दोहराव के लिए, एक विधि जिसमें केवल स्मृति में प्रत्येक पंक्ति की एक ही प्रतिलिपि संग्रहीत करने की आवश्यकता होती है, वह बेहतर प्रदर्शन करेगी। ओवरहेड कुछ व्याख्या के साथ, उसके लिए एक बहुत ही संक्षिप्त awk स्क्रिप्ट है (पहले से ही enzotib द्वारा पोस्ट की गई ):

<input awk '!seen[$0]++'

संक्षिप्त रूप से: !seen[$0] {print} {seen[$0] += 1}अर्थात, वर्तमान लाइन को प्रिंट करें यदि वह अभी तक नहीं देखी गई है, तो seenइस लाइन के लिए काउंटर को बढ़ाएँ (uninitialized चर या सरणी तत्वों का संख्यात्मक मान 0 है)।

लंबी लाइनों के लिए, आप प्रत्येक पंक्ति के केवल गैर-स्पूफेबल चेकसम (जैसे क्रिप्टोग्राफिक डाइजेस्ट) को रखकर स्मृति को बचा सकते हैं। उदाहरण के लिए, SHA-1 का उपयोग करते हुए, आपको केवल प्रति पंक्ति 20 बाइट्स और एक स्थिर ओवरहेड की आवश्यकता होती है। लेकिन कंप्यूटिंग डाइजेस्ट धीमी है; यह विधि केवल तभी जीत पाएगी जब आपके पास तेज़ सीपीयू (विशेष रूप से डिगैस की गणना करने के लिए एक हार्डवेयर त्वरक के साथ) और फाइल के आकार के सापेक्ष बहुत अधिक मेमोरी न हो और पर्याप्त रूप से लंबी लाइनें हों। कोई बुनियादी उपयोगिता आपको प्रत्येक पंक्ति के लिए एक चेकसम की गणना करने की अनुमति नहीं देती है; आपको पर्ल / पायथन / रूबी /… की व्याख्या ओवरहेड सहन करनी होगी या एक समर्पित संकलित कार्यक्रम लिखना होगा।

<input perl -MDigest::MD5 -ne '$seen{Digest::MD5::md5($_)}++ or print' >output

@ गिल्स आपके स्पष्टीकरण के आधार पर awk '!seen[$0]++', क्या इसका मतलब है कि यदि awk 2 डुप्लिकेट लाइनों को देखता है, तो यह हमेशा पहले वाले को रखेगा और बाद के लोगों को अनदेखा करेगा? (या यह अंतिम एक रखेगा?)
user779159

1
@ user779159 यह पहली बार रखता है: प्रत्येक इनपुट लाइन या तो तुरंत मुद्रित होती है (पहली घटना) या बिल्कुल नहीं (बार-बार होने वाली घटना)।
गिल्स

लेकिन यह कैसे सॉर्ट करता है -u ...?
हशवॉडर

@HashWizard एक सादा sort -uक्रम बदलता है। मेरा उत्तर उन समाधानों को दिखाता है जो आदेश को संरक्षित करते हैं (पहली घटनाओं का क्रम, सटीक होना)।
गाइल्स

@ क्या आप कहेंगे कि यह 50% डुप्लिकेट वाली बड़ी फ़ाइलों (10G) के लिए सॉर्ट-यू से अधिक तेज़ है?
हशवॉडर

25
sort -u big-csv-file.csv > duplicates-removed.csv

ध्यान दें कि आउटपुट फ़ाइल को सॉर्ट किया जाएगा।


1
awkअन्य उत्तरों में कमांड जितनी तेज़ नहीं है, लेकिन वैचारिक रूप से सरल है!
जोहान

@ जोहान मैं छोटे न्यूलाइन टर्मिनेटेड स्ट्रिंग्स के सैकड़ों (सम मिलियन) सैकड़ों के साथ फाइलों पर अक्सर यह कर रहा हूं। मैं जो प्रयोग कर रहा हूं, उसके परिणाम मुझे बहुत जल्दी मिलते हैं। यह अधिक महत्वपूर्ण हो सकता है यदि स्क्रिप्ट में उपयोग किया जाता है जो बार-बार चलाए जाते हैं, समय में बचत काफी हो सकती है।
व्लादिस्लाव डोवगलकेस

1
sort -uसॉर्ट के दौरान डुप्लिकेट को हटाने के लिए उपयोग करें , बजाय बाद में। (और मेमोरी बैंडविड्थ बचाता है) इसे दूसरे प्रोग्राम में पाइप करना)। यह केवल awkसंस्करण से बेहतर है यदि आप चाहते हैं कि आपका आउटपुट भी हल हो। (इस सवाल पर ओपी अपने मूल आदेश को संरक्षित रखना चाहता है , इसलिए यह थोड़ा अलग उपयोग के मामले के लिए एक अच्छा जवाब है।)
पीटर कॉर्ड्स

5.5 मिलियन लाइन फ़ाइल (कुल 1.8 जीबी) के लिए, मेरे लिए लगभग एक मिनट का समय लगा। प्रतिभाशाली।
मैक्स विलियम्स

18

मान लें कि आप मेमोरी में डी-डुप्लिकेट की गई फ़ाइल को रख सकते हैं (यदि आपका डेटा वास्तव में 100 के एक कारक द्वारा डुप्लिकेट किया गया है, जो लगभग 20MiB + ओवरहेड होना चाहिए), तो आप पर्ल के साथ यह बहुत आसानी से कर सकते हैं।

$ perl -ne 'print unless $dup{$_}++;' input_file > output_file

यह आदेश को भी संरक्षित करता है।

%dupयदि आप ऐसा चाहते हैं तो हैश से प्रत्येक पंक्ति की घटनाओं की संख्या निकाल सकते हैं , एक अतिरिक्त मुफ्त बोनस के रूप में।

यदि आप पसंद करते हैं awk, तो इसे भी करना चाहिए (पर्ल संस्करण के समान तर्क, उसी क्रम में, dupवैरिएबल में एकत्रित समान डेटा ):

$ awk '{if (++dup[$0] == 1) print $0;}' input_file > output_file

यह बहुत अच्छा है @Mat, मैं फ़ाइल को स्लैप करने वाला था, lol ;-)।
निखिल मुले

अब इंतजार कर रहे हैं @ManAtWork के लिए अपनी सेड और जाग जादू की वीरता के लिए भी :-)
निखिल मुल्ले

भयानक टिप के लिए फिर से भयानक :-)
निखिल मुल्ले

1
क्या केवल अनुलिपि लाइनों को हटाने के लिए पर्ल स्क्रिप्ट को बदलना संभव है?
डंबल्डड

2
@ डंबलडैड: uniqवह सब अपने आप करता है
Mat

3

जैसा कि कोई अन्य उत्तर नहीं दिया गया है, यहाँ कोई समर्थन नहीं है:

gawk -i inplace '!a[$0]++' file

क्या यह आदेश को संरक्षित करता है? वैसे, यह मेरे काम नहीं आया। मेरे संस्करण है:GNU Awk 4.0.2
लियोनिद

1
@ लियोनिड हाँ, यह करता है। यह किसी भी अनोखी रेखा की पहली घटना को छापता है। Inplace समर्थन पहला संस्करण 4.1 है, जो 2013 में जारी किया गया था में शुरू की गई थी
rindeal - जनवरी Chren

3

आप http://www.computerhope.com/unix/uuniq.htm का उपयोग कर सकते हैंuniq

uniq रिपोर्ट या फ़ाइल में दोहराई गई लाइनों को फ़िल्टर करता है।


उत्तर देते समय कुछ स्पष्टीकरण देना बेहतर होगा क्योंकि आपका उत्तर कौन सा है। तो, यह उत्तर पिछले उत्तरों में से कितने से भिन्न है?
स्टीफन राउच

1
यूनीक मैन पेज से: नोट: 'uniq' does not detect repeated lines unless they are adjacent. तो आपको पहले इसे सॉर्ट करना होगा और नॉन डुप्लिकेट लाइनों के ऑर्डर को ढीला करना होगा।
Vindolin

2

पायथन वन लाइनर्स:

python -c "import sys; lines = sys.stdin.readlines(); print ''.join(sorted(set(lines)))" < InputFile

यह पूरी फ़ाइल को स्मृति में धीमा कर देता है और ओपी की समस्या के लिए एक अच्छा फिट नहीं हो सकता है। इसके अलावा आदेश को बनाए रखने की गारंटी नहीं है
iruvar

सुझाव के लिए धन्यवाद, मैं सिर्फ अजगर सीख रहा हूं .. बस सीखने के उद्देश्य के लिए यह कोशिश की है ... :)
राहुल पाटिल

यहां एक पायथन 2.7 संस्करण है जो एक-लाइनर नहीं है (लेकिन पूरी तरह से) अद्वितीय लाइनों को आदेश देता है जो या तो पूरी फाइल को मेमोरी में लोड किए बिना ऑर्डर करता है या प्रिंट करने के लिए फीड करने के लिए एक एकल विशाल स्ट्रिंग
बनाता है

धन्यवाद @ 1_CR मुझे आज कुछ सीखना है :)OrderedDict
राहुल पाटिल

0

यहाँ किसी भी जवाब ने मेरे मैक पर मेरे लिए काम नहीं किया इसलिए मैंने एक साधारण अजगर स्क्रिप्ट लिखी जो मेरे लिए काम करती है। मैं प्रमुख / अनुगामी व्हाट्सएप को अनदेखा कर रहा हूं और स्मृति खपत के बारे में भी परवाह नहीं करता हूं।

import sys

inputfile = sys.argv[1]
outputfile = sys.argv[2]

with open(inputfile) as f:
    content = f.readlines()

content = [x.strip() for x in content]

my_list = list(set(content))

with open(outputfile, 'w') as output:
    for item in my_list:
        output.write("%s\n" % item)

उपर्युक्त को यूनीकडैम में सहेजें और इस तरह से चलाएं:

python unique.py inputfile.txt outputfile.txt

-1

बैश 4 के साथ, एक शुद्ध-बैश समाधान जो साहचर्य सरणियों का लाभ उठाता है, का उपयोग किया जा सकता है। यहाँ एक उदाहरण है

unset llist; declare -A llist;
while read -r line; do
if [[ ${llist[$line]} ]]; then
  continue
else 
  printf '%s\n' "$line"
  llist[$line]="x"
fi
done < file.txt

2
readबड़ी पाठ फ़ाइलों को संसाधित करने के लिए लूप का उपयोग न करें । बैश को एक नई रेखा की निगरानी से बचने के लिए एक-एक-बार-बार पढ़ना पड़ता है। जागने की तुलना में बैश भी सामान्य रूप से टेक्स्ट प्रोसेसिंग में बहुत तेज नहीं है। यदि आप इसका उपयोग करते हैं, तो read -raअपने इनपुट में बैकस्लैश खाने से बचेंगे। इसके अलावा, लूप के unset llist बाद भूल न जाएं , यदि आप इसे शेल फ़ंक्शन में रखते हैं या इसे अंतःक्रियात्मक रूप से उपयोग करते हैं।
पीटर कॉर्ड्स

2
@PeterCordes, या आप बस संदर्भित किया जा सकता था इस :-)
इरुवर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.