टेक्स्ट फ़ाइल से उन पंक्तियों का चयन करें जिनकी आईडी किसी अन्य फ़ाइल में सूचीबद्ध है


13

मैं मध्यम आकार (लगभग 10M-100M लाइनों) टैब से अलग कॉलम टेक्स्ट फ़ाइलों के साथ काम करने के लिए अपने यूनिक्स शेल में बहुत सारे grep awk सॉर्ट का उपयोग करता हूं। इस संबंध में यूनिक्स खोल मेरी स्प्रेडशीट है।

लेकिन मुझे एक बहुत बड़ी समस्या है, वह यह है कि आईडी की सूची में दिए गए रिकॉर्ड का चयन करना।

आईडी की सूची के table.csvसाथ प्रारूप id\tfoo\tbar...और ids.csvफ़ाइल के साथ फाइल करने के बाद , केवल table.csvउपस्थित आईडी से रिकॉर्ड का चयन करें ids.csv

तरह की /programming/13732295/extract-all-lines-from-text-file-based-on-a-given-list-of-ids लेकिन शेल के साथ, पर्ल नहीं।

grep -Fस्पष्ट रूप से गलत पॉजिटिव उत्पन्न करता है यदि आईडी परिवर्तनशील चौड़ाई हैं। joinएक उपयोगिता है जिसका मैं कभी पता नहीं लगा सका। सबसे पहले, इसे अल्फाबेटिक सॉर्टिंग की आवश्यकता होती है (मेरी फाइलें आमतौर पर संख्यात्मक रूप से सॉर्ट की जाती हैं), लेकिन फिर भी मैं गलत ऑर्डर के बारे में शिकायत किए बिना और कुछ रिकॉर्डों को स्किप करने के बिना इसे प्राप्त नहीं कर सकता। इसलिए मुझे यह पसंद नहीं है। gsp -f फाइल के साथ ^id\t-s बहुत धीमी है जब आईडी की संख्या बड़ी है। awkबोझिल है।

क्या इसके लिए कोई अच्छा उपाय हैं? टैब-अलग फ़ाइलों के लिए कोई विशिष्ट उपकरण? अतिरिक्त कार्यक्षमता का भी बहुत स्वागत होगा।

UPD: सही किया गया sort->join


यदि grep -fबहुत धीमा है, तो इस रणनीति को बनाए रखने से अधिक परेशानी की तरह लगता है कि यह मूल्य है - भिन्नताएं संभवतः उसी ओ (एन * एम) प्रदर्शन के मुद्दों का शिकार होंगी। शायद आपका समय बेहतर तरीके से सीखने में व्यतीत होगा कि कैसे एक सामान्यीकृत SQL DB का उपयोग किया जाए ...
गोल्डीलॉक्स

1
आपके द्वारा लिंक किए गए प्रश्न से पर्ल स्क्रिप्ट का उपयोग क्यों नहीं किया गया? वैकल्पिक रूप से, एक समान स्क्रिप्ट को लिखना संभव होना चाहिएawk
cjm

बैश 4 में साहचर्य सरणियां हैं, जो कि आपको नेस्टेड लूप्स को दरकिनार करने की जरूरत है एक ला पर्ल उदाहरण।
गोल्डीलॉक्स

1
sortसभी प्रकार की छंटनी, संख्यात्मक, वर्णमाला और अन्य कर सकते हैं। देखते हैं man sort
टेर्डन

मेरे पास यहां एक प्रश्न है, हम कैसे करते हैं यदि स्रोत फ़ाइल जहां से हम डेटा निकालना चाहते हैं एक गैर-सीमांकित फ़ाइल है

जवाबों:


19

मुझे लगता है कि आपका मतलब grep -fनहीं था , grep -Fलेकिन आपको वास्तव में दोनों के संयोजन की आवश्यकता है और -w:

grep -Fwf ids.csv table.csv

आपके द्वारा झूठी सकारात्मकता प्राप्त करने का कारण यह है (मुझे लगता है, आपने समझाया नहीं था) क्योंकि अगर एक आईडी दूसरे में निहित हो सकती है, तो दोनों को मुद्रित किया जाएगा। -wइस समस्या को दूर -Fकरता है और सुनिश्चित करता है कि आपके पैटर्न को स्ट्रिंग के रूप में माना जाता है, न कि नियमित अभिव्यक्ति। से man grep:

   -F, --fixed-strings
          Interpret PATTERN as a  list  of  fixed  strings,  separated  by
          newlines,  any  of  which is to be matched.  (-F is specified by
          POSIX.)
   -w, --word-regexp
          Select  only  those  lines  containing  matches  that form whole
          words.  The test is that the matching substring must  either  be
          at  the  beginning  of  the  line,  or  preceded  by  a non-word
          constituent character.  Similarly, it must be either at the  end
          of  the  line  or  followed by a non-word constituent character.
          Word-constituent  characters  are  letters,  digits,   and   the
          underscore.

   -f FILE, --file=FILE
          Obtain  patterns  from  FILE,  one  per  line.   The  empty file
          contains zero patterns, and therefore matches nothing.   (-f  is
          specified by POSIX.)

यदि आपकी झूठी सकारात्मकता है क्योंकि एक आईडी एक गैर-आईडी फ़ील्ड में मौजूद हो सकती है, तो इसके बजाय अपनी फ़ाइल के माध्यम से लूप करें:

while read pat; do grep -w "^$pat" table.csv; done < ids.csv

या, तेज:

xargs -I {} grep "^{}" table.csv < ids.csv

perlहालांकि , मैं व्यक्तिगत रूप से ऐसा करूंगा :

perl -lane 'BEGIN{open(A,"ids.csv"); while(<A>){chomp; $k{$_}++}} 
            print $_ if defined($k{$F[0]}); ' table.csv

1
+1 लेकिन: क्या होगा यदि संभावित गलत सकारात्मक हैं जो आईडी को शब्द-वार से मेल खाते हैं, सिर्फ आईडी कॉलम में नहीं? यदि आप ^-F का उपयोग नहीं कर सकते हैं , तो आप पहले कॉलम को विशेष रूप से लक्षित नहीं कर सकते।
गोल्डीलॉक्स

@goldilocks अगर वे बिल्कुल मेल खाते हैं, तो वे झूठी सकारात्मक नहीं हैं। मुझे वह मिलता है जो आप का मतलब है, लेकिन उस स्थिति में, ओपी को अपनी इनपुट फाइलें दिखानी चाहिए।
terdon

^id\tओपी से बिट का तात्पर्य idएक और स्तंभ में हो सकता है। यदि नहीं, तो यह बात नहीं है।
गोल्डीलॉक्स

@goldilocks निष्पक्ष बिंदु, उत्तर संपादित।
terdon

जिस तरह से हम ऐसा करते थे वह अस्थायी फाइलें (awk या sed का उपयोग करके) बनाने के लिए थी, जिसमें एक अनूठा चरित्र (कहते हैं, नियंत्रण-ए) उस क्षेत्र का परिसीमन करना जिसे हम खोजना चाहते थे, फिर grep -F -f temppatternfile का उपयोग करें। tr -d '\ 001'
मार्क प्लॉटनिक

7

joinउपयोगिता आप क्या चाहते है। यह lexically सॉर्ट किए जाने के लिए इनपुट फ़ाइलों की आवश्यकता है।

मान लें कि आपका शेल बैश या ksh है:

join -t $'\t' <(sort ids.csv) <(sort table.csv)

सॉर्ट करने की आवश्यकता के बिना, सामान्य जाग समाधान है

awk -F '\t' 'NR==FNR {id[$1]; next} $1 in id' ids.csv table.csv

जैसा कि मैंने कोशिश की, लेकिन अंत में यह बताने में विफल रहा कि, सम्मिलित होना एक कीचड़ है। मेरे लिए इतनी अच्छी तरह से काम नहीं करता है।
आलमार

1
joinएक कीचड़ नहीं है: आपके शब्द थे आप समझ नहीं सकते थे। अपना दिमाग खोलो और सीखो। आपको क्या आउटपुट मिला, और आप जो उम्मीद करते हैं, उससे कैसे अलग है?
ग्लेन जैकमैन २४'१४

+1, यह एक काम है join
don_crissti

यहाँ awkसमाधान मेरे उद्देश्यों के लिए बहुत जल्दी और कुशल है (मैं ~ 100M लाइनों के साथ फाइलों से कुछ सौ के सबसेट निकाल रहा हूँ)
ल्यूक

2

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

इसलिए, अपने अलग चरित्र को निर्दिष्ट करने के लिए सॉर्ट का -t विकल्प का उपयोग करें, और फ़ील्ड को निर्दिष्ट करने के लिए -k विकल्प का उपयोग करें (यह याद रखते हुए कि आपको एक शुरुआत और अंत फ़ील्ड की आवश्यकता है - भले ही यह एक ही हो - या यह उस वर्ण से सॉर्ट करेगा। पंक्ति के अंत तक)।

तो इस प्रश्न की तरह टैब से अलग की गई फ़ाइल के लिए, निम्नलिखित को काम करना चाहिए ( संरचना के लिए ग्लेन के उत्तर के लिए धन्यवाद ):

join -t$'\t' <(sort -d ids.csv) <(sort -d -t$'\t' -k1,1 table.csv) > output.csv

(संदर्भ के लिए, -d ध्वज का अर्थ शब्दकोश सॉर्ट है। आप प्रमुख व्हाट्सएप को देखने के लिए -b ध्वज का उपयोग करना चाह सकते हैं, देखें man sortऔर man join)।

एक और सामान्य उदाहरण के रूप में, मान लीजिए कि आप दो अल्पविराम से अलग की गई फ़ाइलों में शामिल हो रहे हैं - input1.csvतीसरे स्तंभ input2.csvपर और चौथे पर। आप उपयोग कर सकते हैं

join -t, -1 3 -2 4 <(sort -d -t, -k3,3 input2.csv) <(sort -d -t, -k4,4 input2.csv) > output.csv

यहां -1और -2विकल्प निर्दिष्ट करते हैं कि कौन सी फ़ील्ड क्रमशः पहली और दूसरी इनपुट फ़ाइलों में शामिल होनी है।


0

आप रूबी का उपयोग कुछ समान करने के लिए भी कर सकते हैं:

ruby -pe 'File.open("id.csv").each { |i| puts i if i =~ /\$\_/ }' table.csv
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.