ลบบรรทัดทั้งหมดในไฟล์ A ซึ่งมีสตริงในไฟล์ B


15

ฉันมีไฟล์ CSV 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ฉันมีรายการหมายเลขผู้ใช้:

30923833
77392318

มีวิธีที่ฉลาดและมีประสิทธิภาพในการลบแถวทั้งหมดออกจากusers.csvไฟล์ที่มีรหัสtoremove.txtหรือไม่ ฉันได้เขียนแอพ Python อย่างง่ายเพื่อแยกไฟล์สองไฟล์และเขียนลงในไฟล์ใหม่เฉพาะบรรทัดที่ไม่พบtoremove.txtแต่มันช้ามากเป็นพิเศษ บางทีเวทมนตร์sedหรือบางอย่างawkอาจช่วยได้ที่นี่

นี่คือผลลัพธ์ที่ต้องการโดยพิจารณาจากตัวอย่างด้านบน:

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

บางทีคุณควรแบ่งปันสคริปต์หลามของคุณ ฉันสงสัยว่ามีบางอย่างผิดปกติที่นั่นเช่น O (N²) แม้ว่าคุณจะเก็บและลบเวทมนตร์ที่มีอยู่นับล้านระเบียนจะไม่ช่วยอะไรมากนัก
Ángel

สคริปต์ที่ในความเป็นจริง O (n <sup> 2 </ sup>): n สำหรับusers.csvบรรทัดของไฟล์และ n toremove.txtสำหรับสายของ ฉันไม่แน่ใจจริงๆว่าจะทำอย่างไรกับความซับซ้อนที่ลดลง for u in users: if not any(toremove in u): outputfile.write(u)สรุปสาระสำคัญของมันคือ ฉันสามารถโพสต์ไว้ที่ Code Review
dotancohen

1
ฉันจะอ่านtoremove.txtประหยัดรายการเป็นคีย์ ทำซ้ำ users.csv พิมพ์ที่ id ไม่อยู่ใน dict คุณได้รับการประมวลผล O (n) สำหรับทั้งtoremove.txtและusers.csvและการใช้หน่วยความจำ O (n) สำหรับtoremove.txt(ซึ่งอาจจะค่อนข้างเล็ก)
Ángel

@ Ángel: ใช่นั่นเป็นวิธีที่สคริปต์ทำงาน!
dotancohen

1
การตรวจสอบว่ามีคีย์อยู่ในพจนานุกรมหรือไม่เท่ากับการตรวจสอบตารางแฮชซึ่งก็คือ (เกือบ) O (1) ในทางกลับกันหากจำเป็นต้องย้ำรายการที่จะลบนั่นคือ O (m)
Ángel

คำตอบ:


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, …)
สกอตต์

@Scott: ใช่มันเป็นเหตุผลที่ฉันวางawkวิธีการแก้ปัญหาหลังgrep
cuonglm

4

ต่อไปนี้เป็นawkคำตอบของ Gnouc ซึ่งได้รับการปรับแต่งให้เป็นสเปซ - บอด:

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

เพราะมันใช้เพียงเครื่องหมายจุลภาค (และไม่เว้นวรรค) เป็นตัวคั่น $1เป็น"John Lennon", $2เป็น 90123412(มีพื้นที่ชั้นนำ) ฯลฯ ดังนั้นเราจะใช้gensubในการลบจำนวนช่องว่างนำใด ๆ จาก$2 ก่อนที่เราจะตรวจสอบว่า (หมายเลขผู้ใช้) อยู่ในtoremove.txtไฟล์


คุณอาจจะสามารถทำสิ่งที่ฉลาดอื่น ๆ ที่นี่ (แค่คิดออกมาดัง ๆ ) เช่นการแยก "ส่วนที่แน่นอน" ของสตริงที่ไม่ควรจับคู่และเปรียบเทียบกับอาเรย์ที่เชื่อมโยงหรืออะไรที่ไม่
rogerdpack

ฉันเชื่อว่าเป็นสิ่งที่ฉันทำ สิ่งที่คุณมีในใจ?
สกอตต์

ใช่คุณเป็น ฉันแค่อ้างถึงว่าคุณจำเป็นต้องทำอะไรที่ขี้ขลาดมากกว่าเช่นลบครึ่งแรกของบรรทัดหรืออะไรทำนองนั้น (การลดขนาด ฯลฯstackoverflow.com/a/4784647/32453 ) เพียงการแจงเฉพาะ
rogerdpack

0

ตกลงวิธีทับทิม: หากคุณมีรายการสตริงในไฟล์และคุณต้องการลบบรรทัดทั้งหมดออกจากไฟล์อื่นที่มีสตริงใด ๆ ในไฟล์แรก (ในกรณีนี้ให้ลบ "file2" จาก "file1") ไฟล์ทับทิม :

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

น่าเสียดายที่ไฟล์ "ลบ" ที่มีขนาดใหญ่ดูเหมือนว่าจะลดความซับซ้อนไปยัง O (N ^ 2) (สมมติฐานของฉันคือ regexp มีงานให้ทำมากมาย) แต่ก็อาจเป็นประโยชน์กับคนที่อยู่ข้างนอก (ถ้าคุณ ต้องการมากกว่าการลบบรรทัดเต็ม) อาจเร็วกว่าในบางกรณี

ตัวเลือกอื่นหากคุณต้องการความเร็วคือการใช้กลไกการตรวจสอบแฮชเดียวกัน แต่เพื่อ "แยกวิเคราะห์" บรรทัดสำหรับสตริงที่อาจตรงกันจากนั้นเปรียบเทียบกับแฮชของคุณ

ในทับทิมอาจมีลักษณะเช่นนี้:

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

ดูคำตอบของ Scott เช่นเดียวกับคำตอบ awk ที่เสนอที่นี่และหลีกเลี่ยงความซับซ้อน O (N ^ 2) (วุ้ย)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.