फ़ाइल A में वे सभी पंक्तियाँ निकालें जिनमें फ़ाइल B में स्ट्रिंग्स हैं


15

मेरे पास users.csvउपयोगकर्ता नाम, उपयोगकर्ता नाम और अन्य डेटा की एक सूची के साथ एक सीएसवी फ़ाइल है :

username, userid, sidebar_side, sidebar_colour
"John Lennon", 90123412, "left", "blue"
"Paul McCartny", 30923833, "left", "black"
"Ringo Starr", 77392318, "right", "blue"
"George Harrison", 72349482, "left", "green"

एक अन्य फ़ाइल में toremove.txtमेरे पास userIDs की एक सूची है:

30923833
77392318

क्या users.csvफ़ाइल से सभी पंक्तियों को निकालने का एक चतुर, कुशल तरीका है जिसमें आईडी शामिल हैं toremove.txt? मैंने दो फ़ाइलों को पार्स करने के लिए एक साधारण पायथन ऐप लिखा है और एक नई फ़ाइल में केवल उन पंक्तियों को लिखा है जो इसमें नहीं पाई जाती हैं toremove.txt, लेकिन यह असाधारण रूप से धीमी है। शायद कुछ sedया awkजादू यहाँ मदद कर सकता है?

यह वांछित परिणाम है, ऊपर के उदाहरणों पर विचार करते हुए:

username, userid, sidebar_side, sidebar_colour
"John Lennon", 90123412, "left", "blue"
"George Harrison", 72349482, "left", "green"

हो सकता है कि आपको अपनी अजगर की स्क्रिप्ट साझा करनी चाहिए। मुझे संदेह है कि O (N Although) होने के नाते कुछ गड़बड़ है, हालांकि यदि आप लाखों रिकॉर्ड रख रहे हैं और निकाल रहे हैं तो जादू बहुत ज्यादा मदद नहीं करेगा।
Ángel

स्क्रिप्ट वास्तव में O (n <सुप> 2 </ sup>) है: n users.csvफ़ाइल की लाइनों के लिए, और n लाइनों के लिए toremove.txt। मुझे वास्तव में यकीन नहीं है कि इसे कम जटिलता के साथ कैसे किया जाए। इसका सार है for u in users: if not any(toremove in u): outputfile.write(u):। मैं इसे कोड समीक्षा में पोस्ट कर सकता हूं।
dotancohen

1
मैं पढ़ता हूँ toremove.txt, प्रविष्टियों को कुंजियों के रूप में सहेजना । Iterate users.csv, उन लोगों को प्रिंट करना जहां आईडी तानाशाह में नहीं है। आप दोनों के लिए मिलता हे (एन) के प्रसंस्करण toremove.txtऔर users.csv, और के लिए हे (एन) स्मृति उपयोग toremove.txt(जो शायद अपेक्षाकृत छोटा है)
एंजेल

@ Scriptngel: हाँ, यह ठीक है कि स्क्रिप्ट कैसे काम करती है!
dotancohen

1
जाँच करना कि क्या कोई कुंजी किसी शब्दकोश में मौजूद है, हैश तालिका जाँच के बराबर है, जो (लगभग) O (1) है। दूसरी ओर, अगर इसे हटाने के लिए आइटम्स को पुनरावृत्त करना है, तो वह है O (m)
17ngel

जवाबों:


15

के साथ grep, आप कर सकते हैं:

$ grep -vwF -f toremove.txt users.txt 
username, userid, sidebar_side, sidebar_colour
"John Lennon", 90123412, "left", "blue"
"George Harrison", 72349482, "left", "green"

के साथ awk:

$ awk -F'[ ,]' 'FNR==NR{a[$1];next} !($4 in a)' toremove.txt users.txt 
username, userid, sidebar_side, sidebar_colour
"John Lennon", 90123412, "left", "blue"
"George Harrison", 72349482, "left", "green"

@terdon: डांग! मै वह कहने जा रहा था। ध्यान दें, हालांकि, कि Gnouc का उत्तर (यकीनन) वही करता है जो सवाल पूछता है , लेकिन यह वह नहीं हो सकता जो उपयोगकर्ता चाहता है।
स्कॉट

awkसमाधान अत्यधिक फ़ाइलें 'स्वरूपित किया जा रहा के प्रति संवेदनशील है बिल्कुल के रूप में सवाल में दिखाया गया है। सबसे ज्यादा गौर से, अगर कोई नाम सिर्फ एक शब्द / टोकन (यानी, इसमें कोई रिक्त स्थान नहीं है; उदाहरण के लिए, "Bono") या दो से अधिक टोकन हैं (यानी, इसमें एक से अधिक स्थान हैं; उदाहरण के लिए "Sir Paul McCartney"), यह तब भी गुजरता है, भले ही उपयोगकर्ता का मिलान कम स्पष्ट रूप से, एक ही बात होती है यदि पहले अल्पविराम और उपयोगकर्ता के बीच कोई स्थान नहीं है, या यदि एक से अधिक स्थान हैं (जैसे, "John Lennon", 90123412, …)।
स्कॉट

awkgrep
@ सच

4

यहाँ Gnouc है awk उत्तर दिया गया है, जिसे अंतरिक्ष-अंधा होने के लिए संशोधित किया गया है:

awk -F, 'FNR==NR{a[$1];next} !(gensub("^ *","",1,$2) in a)' toremove.txt users.csv

यह डिलीमीटर के रूप में केवल अल्पविराम (और नहीं रिक्त स्थान) का उपयोग करता है के बाद से, $1है "John Lennon", $2है  90123412, आदि तो हम का उपयोग करें (एक प्रमुख स्थान के साथ) gensubसे प्रमुख स्थानों में से किसी भी संख्या को दूर करने $2 से पहले हम यह देखना होगा कि यह (userid) में था toremove.txtफ़ाइल।


आप यहां कुछ अन्य चतुर सामान करने में सक्षम हो सकते हैं (केवल ज़ोर से सोचकर) जैसे कि स्ट्रिंग के "सटीक टुकड़े" को बाहर निकालने के लिए, जो कि मेल नहीं होना चाहिए, और साहचर्य सरणी के साथ तुलना करें, या क्या नहीं।
रोजरपैक

मुझे विश्वास है कि मैं यही कर रहा हूं। आप के मन में क्या था?
स्कॉट

हाँ तुम हो। मैं सिर्फ इस बात का जिक्र कर रहा था कि क्या आपको कुछ और फंकी करने की जरूरत है जैसे कि लाइन के पहले हाफ को हटाना या ऐसा कुछ भी (डाउनकास्टिंग, आदि ) stackoverflow.com/a/4784647/32453 ) केवल विशेष पार्सिंग
रोसेडपैक

0

माणिक तरीके से ठीक करें: यदि आपके पास किसी फ़ाइल में तार की सूची है, और आप किसी अन्य फ़ाइल से सभी पंक्तियों को निकालना चाहते हैं, जिसमें पहली फ़ाइल में कोई भी स्ट्रिंग हो (इस मामले में "file1" से "file2" को हटाते हुए) माणिक फ़ाइल :

b=File.read("file2").split # subtract this one out
remove_regex = Regexp.new(b.join('|'))
File.open("file1", "r").each_line do |line|
  if line !~ remove_regex
    puts line
  end
end

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

एक अन्य विकल्प यदि आप गति के लिए जा रहे हैं तो उसी हैश चेकिंग तंत्र का उपयोग करना है, लेकिन ध्यान से "पार्स" लाइन को स्ट्रिंग्स के लिए मेल कर सकते हैं जो कि आपके हैश के साथ तुलना कर रहा है।

रूबी में, इस तरह लग सकता है:

b=File.read("file2").split # subtract this one out
hash={}
for line in b
  hash[line] = 1
end

ARGF.each_line do |line|
  ok = true
  for number in line.scan(/\d{9}/)
    if hash.key? number
      ok=false
    end
  end
  if (ok)
    puts line
  end
end

स्कॉट के उत्तर को भी देखें, उसके अजीब जवाबों के समान ही, यहां प्रस्तावित है, और ओ (एन ^ 2) जटिलता (पाव) से बचा जाता है।

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