删除文件A中包含文件B中字符串的所有行


15

我有一个CSV文件users.csv,其中包含用户名,用户ID和其他数据的列表:

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我有一个用户ID列表:

30923833
77392318

有没有巧妙,有效的方法从users.csv文件中删除包含ID的所有行toremove.txt?我编写了一个简单的Python应用程序来解析这两个文件,并仅将在中找不到的那些行写入一个新文件toremove.txt,但是这非常慢。也许有些sedawk魔术可以帮助这里?

考虑以上示例,这是理想的结果:

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

也许您应该共享您的python脚本。我怀疑那里出了点问题,例如O(N²),尽管如果要保留和删除数百万条记录,魔术不会有太大帮助。
安赫尔

该脚本实际上是O(n <sup> 2 </ sup>):n用于users.csv文件的行,n用于的行toremove.txt。我不太确定如何以较低的复杂度进行操作。要点是:for u in users: if not any(toremove in u): outputfile.write(u)。我可以将其发布到代码审查。
dotancohen 2014年

1
我会读toremove.txt,将条目保存为。迭代users.csv,打印id不在字典中的那些文件。你得到O(n)的处理两者toremove.txtusers.csv,以及O(n)的内存使用toremove.txt(这可能是比较小)
安赫尔

@Ángel:是的,这正是脚本的工作原理!
dotancohen 2014年

1
检查关键字是否存在于字典中,等同于哈希表检查,即(几乎)O(1)。在另一方面,如果它需要迭代的项目去掉,这是(M)O
安赫尔

Answers:


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:ang!我要说的是。但是请注意,Gnouc的答案(可以说)确实满足了问题的要求,但这可能并不是用户想要的。
斯科特

awk解决方案对问题所显示的文件格式完全敏感。最明显的是,如果名称只是一个单词/令牌(即,它不包含空格;例如"Bono")或超过两个令牌(即,它包含多个空格;例如"Sir Paul McCartney"),即使用户名匹配。不太明显的是,如果第一个逗号和用户ID之间没有空格,或者如果有多个空格(例如"John Lennon", 90123412, …),则会发生相同的情况。
Scott

@斯科特:是的,这就是我awk落后于解决方案的原因grep
cuonglm

4

这是Gnouc的awk答案,被修改为对太空盲目的:

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

由于它仅使用逗号(而不是空格)作为定界符,所以 $1is "John Lennon"$2is  90123412(带有前导空格)等。因此gensub$2 在检查文件中是否包含(用户ID)之前,我们通常使用删除其中的任何前导空格toremove.txt


您也许可以在这里做一些其他聪明的事情(只是大声思考),例如解析出不应该匹配的字符串的“精确部分”,然后将其与关联数组进行比较,或者不进行比较。
rogerdpack 2015年

我相信这就是我正在做的。你有什么想法?
斯科特

是的,你是。我只是指的是,如果你需要做一些更时髦如消除线路或类似的东西(downcasing等上半年stackoverflow.com/a/4784647/32453)刚刚专业解析
rogerdpack

0

用红宝石的方式行事:如果文件中有一个字符串列表,并且您想从另一个文件中删除所有行,甚至在第一个文件中也包含任何字符串(在这种情况下,从“ file1”中删除“ file2”),ruby文件:

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)(我认为正则表达式有很多工作要做),但对于那里的某个人仍然可能有用(如果您想要的不只是删除完整的行)。在某些情况下可能会更快。

如果要提高速度,另一种选择是使用相同的哈希检查机制,但要仔细“解析”行中可能匹配的字符串,然后将其与哈希进行比较。

在红宝石中,可能看起来像这样:

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)复杂度(phew)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.