Files

如何刪除文本文件中的重複行?

  • January 11, 2022

我的一個巨大(最多 2 GiB)文本文件包含其中每一行的大約 100 個精確副本(在我的情況下沒用,因為該文件是一個類似 CSV 的數據表)。

我需要的是刪除所有重複,同時(最好,但這可以犧牲顯著的性能提升)保持原始序列順序。結果,每一行都是唯一的。如果有 100 行相等的行(通常重複的行分佈在文件中並且不會是鄰居),則只剩下一種。

我已經用 Scala 編寫了一個程序(如果您不了解 Scala,可以考慮使用 Java)來實現這一點。但也許有更快的 C 編寫的本機工具能夠更快地做到這一點?

更新:awk '!seen[$0]++' filename只要文件接近 2 GiB 或更小,該解決方案對我來說似乎工作得很好,但現在我要清理一個 8 GiB 文件,它不再工作了。在具有 4 GiB RAM 的 Mac 和具有 4 GiB RAM 和 6 GiB 交換的 64 位 Windows 7 PC 上,它似乎是無窮大的,只是記憶體不足。鑑於這種經驗,我對在具有 4 GiB RAM 的 Linux 上嘗試它並不感興趣。

awk在#bash(Freenode)上看到的解決方案:

awk '!seen[$0]++' filename

如果要就地編輯文件,可以使用以下命令(前提是您使用實現此擴展的 GNU awk 版本):

awk -i inplace '!seen[$0]++' filename

有一個使用標準實用程序的簡單(不是很明顯)方法,除了執行之外不需要大記憶體sort,在大多數實現中,它對大文件有特定的優化(一個好的外部排序算法)。這種方法的一個優點是它只遍歷專用實用程序內的所有行,而不是解釋語言內的所有行。

<input nl -b a -s : |           # number the lines
sort -t : -k 2 -u |             # sort and uniquify ignoring the line numbers
sort -t : -k 1n |               # sort according to the line numbers
cut -d : -f 2- >output          # remove the line numbers

如果所有行都以非空白字元開頭,則可以省略一些選項:

<input nl | sort -k 2 -u | sort -k 1n | cut -f 2- >output

對於大量重複,只需要在記憶體中儲存每行的單個副本的方法會執行得更好。通過一些解釋成本,有一個非常簡潔的 awk 腳本(已經由 enzotib 發布):

<input awk '!seen[$0]++'

不太簡潔:!seen[$0] {print} {seen[$0] += 1},即如果尚未看到目前行,則列印目前行,然後seen為該行增加計數器(未初始化的變數或數組元素的數值為 0)。

對於長行,您可以通過僅保留每行的不可欺騙校驗和(例如加密摘要)來節省記憶體。例如,使用 SHA-1,您只需要 20 個字節加上每行的固定成本。但是計算摘要相當慢;這種方法只有在你有一個快速的 CPU(尤其是一個帶有硬體加速器來計算摘要的 CPU)並且相對於文件大小和足夠長的行沒有很多記憶體的情況下才會獲勝。沒有基本實用程序可以讓您計算每行的校驗和;你必須承擔 Perl/Python/Ruby/… 的解釋成本,或者編寫一個專門的編譯程序。

<input perl -MDigest::MD5 -ne '$seen{Digest::MD5::md5($_)}++ or print' >output

引用自:https://unix.stackexchange.com/questions/30173