在命令行上从文件中删除多个字符串,高性能

在命令行上从文件中删除多个字符串,高性能

是否有一种优雅、高性能的单行方法可以从输入中删除多个完整字符串?

我处理大型文本文件,例如 100 万行输入文件,以及 100k 个匹配字符串命中文件。我有一个加载的 perl 脚本命中文件到一个散列中,然后检查每行中的所有“单词”输入文件,但对于我的工作流程,我更喜欢一个简单的命令而不是我的脚本。

我寻求的功能相当于:

perl -pe 's/\b(string1|string2|string3)\b)//g' 

或嵌套 sed 的这种方法:

sed -e "$(sed 's:.*:s/&//ig:' hitfile)" inputfile

或在 shell 中循环:

while read w; do sed -i "s/$w//ig" hitfile ; done < inputfile

但那些太贵了。这种稍微高效的方法有效(如何从文本文件中删除所有出现的单词列表?)但它仍然很慢:

perl -Mopen=locale -Mutf8 -lpe '
  BEGIN{open(A,"hitfile"); chomp(@k = <A>)} 
  for $w (@k){s/(^|[ ,.—_;-])\Q$w\E([ ,.—_;-]|$)/$1$2/ig}' inputfile

但是还有其他技巧可以更简洁地做到这一点吗?我忽略了其他一些 unix 命令或方法?我不需要正则表达式,我只需要将纯/精确字符串与哈希值进行比较(为了速度)。即“pine”不应该匹配“pineapple”,但它应该匹配“(pine)”。

例如,我的一个想法是将文件中的单词扩展为单独的行

前:

Hello, world!

后:


Hello
, 
world
!

然后使用 grep -vf 进行处理,然后重新构建/连接这些行。

还有其他既快速又简单的想法吗?

答案1

你的具体有多大hitfile?您能展示一些您正在尝试做的事情的实际例子吗?由于您没有提供有关输入数据的更多详细信息,因此这是只是一个想法尝试并根据您的真实数据进行基准测试

Perl 正则表达式能够变得相当大,并且单个正则表达式将允许您一次性修改输入文件。在这里,我使用/usr/share/dict/words作为构建巨大正则表达式的示例,我的正则表达式有约 99k 行,大小约 1MB。

use warnings;
use strict;
use open qw/:std :encoding(UTF-8)/;

my ($big_regex) = do {
    open my $wfh, '<', '/usr/share/dict/words' or die $!;
    chomp( my @words = <$wfh> );
    map { qr/\b(?:$_)\b/ } join '|', map {quotemeta}
        sort { length $b <=> length $a or $a cmp $b } @words };

while (<>) {
    s/$big_regex//g;
    print;
}

我不需要正则表达式,我只需要将纯/精确字符串与哈希值进行比较(为了速度)。即“pine”不应该匹配“pineapple”,但应该匹配“(pine)”。

如果“pine”不应该匹配“pineapple”,则还需要检查输入中“pine”出现之前和之后的字符。虽然使用固定字符串方法当然是可能的,但它听起来像是单词边界的正则表达式概念(\b)就是你所追求的。

是否有一种优雅的、高性能的单行方式......对于我的工作流程,我更喜欢一个简单的命令而不是我的脚本。

我不确定我是否同意这种观点。有什么问题吗perl script.pl?您可以将它与 shell 重定向/管道一起使用,就像单行代码一样。将代码放入脚本中将使您的命令行变得整洁,并允许您执行复杂的操作,而无需尝试将所有内容都塞进一行代码中。另外,短并不一定意味着快。

您可能想要使用脚本的另一个原因是如果您有多个输入文件。使用上面显示的代码,构建正则表达式相当昂贵,因此多次调用脚本将非常昂贵 - 在单个脚本中处理多个文件将消除该开销。我喜欢 UNIX 原理,但对于大数据来说,调用多个进程(有时多次)并在它们之间通过管道传送数据并不总是最有效的方法,在单个程序中将其全部简化可能会有所帮助。


更新:根据评论,绳子足够搬起石头砸自己的脚

相关内容