【问题标题】:Efficiently remove lines from fileA that contains string from fileB有效地从文件中删除包含文件中字符串的行
【发布时间】:2014-04-24 23:20:51
【问题描述】:

FileA 包含行 FileB 包含单词

我如何有效地从 FileB 中删除包含在 FileA 中找到的单词的行?

我尝试了以下方法,但我什至不确定它们是否有效,因为它需要很长时间才能运行。

试过grep:

grep -v -f <(awk '{print $1}' FileB.txt) FileA.txt > out

也试过python:

f = open(sys.argv[1],'r')
out = open(sys.argv[2], 'w')
bad_words = f.read().splitlines()

with open('FileA') as master_lines:
  for line in master_lines:
    if not any(bad_word in line for bad_word in bad_words):
      out.write(line)

文件A:

abadan refinery is one of the largest in the world.
a bad apple spoils the barrel.
abaiara is a city in the south region of brazil.
a ban has been imposed on the use of faxes

文件B:

abadan
abaiara

期望的输出:

a bad apple spoils the barrel.
a ban has been imposed on the use of faxes

【问题讨论】:

  • 你的文件有多大?
  • FileA with lines 有3M 行,FileB with the keywords 约200k
  • @user1899415 你能从两个文件的样本数据中发布吗?另外,请确保您的文件没有 Windows 格式。您可以使用 dos2unix 实用程序将它们转换为。
  • @jaypal 已编辑以包含示例数据和所需的输出
  • 将 FileB 加载到 python setdict 将允许您进行更快的查找,所以我希望如果您这样做,结果会更好。顺便说一句,您在示例中混合了 FileA 和 FileB。

标签: python perl bash unix


【解决方案1】:

我拒绝相信 Python 在这方面的性能至少无法与 Perl 相提并论。这是我对在 Python 中解决此问题的更有效版本的快速尝试。我正在使用sets 来优化这个问题的搜索部分。 & 运算符返回一个新集合,其中包含两个集合共有的元素。

这个解决方案需要 12 秒才能在我的机器上运行一个 3M 行的 fileA 和一个 200k 字的 fileB,而 perl 需要 9 秒。最大的减速似乎是 re.split,它似乎比字符串快。在这种情况下分裂。

如果您有任何提高速度的建议,请评论此答案。

import re

filea = open('Downloads/fileA.txt')
fileb = open('Downloads/fileB.txt')

output = open('output.txt', 'w')
bad_words = set(line.strip() for line in fileb)

splitter = re.compile("\s")
for line in filea:
    line_words = set(splitter.split(line))
    if bad_words.isdisjoint(line_words):
        output.write(line)

output.close()

【讨论】:

  • 当然。如果我还有剩余电池。如果我的笔记本电脑坏了,我明天再做。 perl 解决方案正在消耗大量的汁液(仍在运行);)...如果您想重新创建数据,请下载 norvig.com/big.txt(大约 100,000 行)并使用 cat 和 >> 复制它。抓取一个单词列表并使用 head -c200k 将其截断为 200k。这应该会为您提供与我正在测试的相同大小的数据。
  • @jaypal 我不保证我的 Python 解决方案的正确性。它适用于原始用户发布的示例数据。对于更大的数据集,我没有仔细研究它。显然,标点符号也不算在内。
  • 在我的机器 line_words = set(line.split("\s")) 上使用常规的 spit 代替正则表达式对象快 4 倍。我正在使用Python 2.7.3
  • @jaypal 干得好...更新的 perl 脚本需要 9 秒。我尝试更新我的 python 解决方案以使用生成器,但这只节省了一秒钟。在我的 Python 解决方案中,最大的时间消耗是拆分字符串。有没有人想过如何优化那部分?
  • @jaypal 不用担心!您的解决方案仍然领先 /w 9 秒。我阅读了 Python 中 set 模块的手册,并使用 isdisjoint 消除了每次调用 len 的需要。这使我的解决方案缩短到 12 秒。稍后我会再搞砸这个......我拒绝让 Perl 获胜,即使只有 3 秒的差异 :)
【解决方案2】:

您的命令看起来不错,因此可能是时候尝试一种好的脚本语言了。尝试运行以下perl 脚本,看看它是否更快地报告回来。

#!/usr/bin/perl

#use strict;
#use warnings;

open my $LOOKUP, "<", "fileA" or die "Cannot open lookup file: $!";
open my $MASTER, "<", "fileB" or die "Cannot open Master file: $!";
open my $OUTPUT, ">", "out" or die "Cannot create Output file: $!";

my %words;
my @l;

while (my $word = <$LOOKUP>) {
    chomp($word);
    ++$words{$word};
}

LOOP_FILE_B: while (my $line = <$MASTER>) {
    @l = split /\s+/, $line;
        for my $i (0 .. $#l) {
            if (defined $words{$l[$i]}) {
                next LOOP_FILE_B;
            }
        }
    print $OUTPUT "$line"
}

【讨论】:

  • 哇,这太神奇了,它运行并在大约 10 秒内完成!
  • @user1899415 我对脚本进行了更改。第一个版本只查看与行首匹配的单词。根据您的示例数据,我误解了您的要求。更新版本从单词文件中查找匹配单词的整行。请再次运行测试,因为您的初始输出可能不正确。
【解决方案3】:

使用 grep

grep -v -Fwf fileB fileA

【讨论】:

  • python 解决方案采用0m1.053s,但您的grep 解决方案采用0m0.608s 和我正在使用的文件。我肯定会使用这个解决方案来提高效率。
  • 感谢您的反馈。
猜你喜欢
  • 2013-10-27
  • 2021-08-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-30
  • 1970-01-01
  • 2018-06-28
相关资源
最近更新 更多