在命令行從文件中刪除多個字元串,高性能
是否有一種優雅、高性能的單行方式從輸入中刪除多個完整的字元串?
我處理大型文本文件,例如inputfile中的 100 萬行和**hitfile中的 100k 匹配字元串。我有一個 perl 腳本,它將hitfile載入到雜湊中,然後檢查inputfile的每一行中的所有“單詞” ,但對於我的工作流程,我更喜歡腳本的簡單命令。
我尋求的功能等同於:
perl -pe 's/\b(string1|string2|string3)\b)//g'
或這種嵌套 sed 的方法:
sed -e "$(sed 's:.*:s/&//ig:' hitfile)" inputfile
或在外殼中循環:
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 處理,然後重新建構/加入行。
還有其他可以快速輕鬆地執行的想法嗎?
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 原則,但對於大數據,呼叫多個程序(有時多次)並在它們之間傳遞數據並不總是最有效的方法,在一個程序中簡化所有程序會有所幫助。
更新:根據評論,足夠的繩子可以射中自己的腳😉程式碼與上面的單行程式碼相同:
perl -CDS -ple 'BEGIN{local$/;($r)=map{qr/\b(?:$_)\b/}join"|",map{quotemeta}sort{length$b<=>length$a}split/\n/,<>}s/$r//g' /usr/share/dict/words input.txt