Mój ogromny plik tekstowy (do 2 GiB) zawiera około 100 dokładnych duplikatów każdego wiersza w nim (w moim przypadku jest to bezużyteczne, ponieważ jest to tabela danych podobna do CSV).
To, czego potrzebuję, to usunięcie wszystkich powtórzeń, podczas gdy (najlepiej, ale można to poświęcić w celu znacznego zwiększenia wydajności) przy zachowaniu oryginalnej kolejności sekwencji. W rezultacie każda linia ma być unikalna. Jeśli było 100 równych wierszy (zwykle duplikaty są rozłożone w pliku i nie będą sąsiadami), pozostanie tylko jeden taki rodzaj.
Napisałem program w Scali (rozważ to Java, jeśli nie wiesz o Scali), aby to zaimplementować. Ale może są szybsze natywne narzędzia napisane w C, które mogą to zrobić szybciej?
AKTUALIZACJA: awk '!seen[$0]++' filename
rozwiązanie wydawało się działać dobrze dla mnie, dopóki pliki były w pobliżu 2 GiB lub mniejszych, ale teraz, gdy mam wyczyścić plik 8 GiB, to już nie działa. Wydaje się, że zabiera nieskończoność na komputerze Mac z 4 GiB RAM i 64-bitowym Windows 7 PC z 4 GiB RAM i 6 GiB swap po prostu kończy się pamięć. I nie czuję entuzjazmu, próbując tego na Linuksie z 4 GiB RAM biorąc pod uwagę to doświadczenie.
sort -u
prawdopodobnie będzie szybszy.Odpowiedzi:
awk
Rozwiązanie widoczne na #bash (Freenode):źródło
awk
wersja z użyciem 2 wyszukiwań tablicowych (pokazanych jako wyjaśnienie w odpowiedzi Gillesa): 0m36.132s vs 0m49.958s .. dla 50 milionów linii .. Myślałem, że wąskim gardłem będzie I / O, ale dodatkowe wyszukiwanie tablic to ... milion elementów w tablicy wydaje się robić znaczące wgniecenie ...Istnieje prosta (co nie jest oczywiste) metoda wykorzystująca standardowe narzędzia, które nie wymagają dużej pamięci poza uruchomieniem
sort
, która w większości implementacji ma określone optymalizacje dla dużych plików (dobry algorytm sortowania zewnętrznego). Zaletą tej metody jest to, że zapętla ona tylko wszystkie linie wewnątrz narzędzi specjalnego przeznaczenia, nigdy wewnątrz interpretowanych języków.Jeśli wszystkie wiersze zaczynają się od spacji, możesz zrezygnować z niektórych opcji:
W przypadku dużej ilości duplikacji metoda, która wymaga tylko przechowywania pojedynczej kopii każdej linii w pamięci, działa lepiej. Po pewnym nakładzie interpretacyjnym istnieje bardzo zwięzły skrypt awk (już opublikowany przez enzotib ):
Mniej zwięźle:
!seen[$0] {print} {seen[$0] += 1}
tzn. Wydrukuj bieżącą linię, jeśli jeszcze nie była widoczna, następnie zwiększseen
licznik dla tej linii (niezainicjowane zmienne lub elementy tablicy mają wartość liczbową 0).W przypadku długich linii można zaoszczędzić pamięć, przechowując tylko niepodlegającą fałszowaniu sumę kontrolną (np. Streszczenie kryptograficzne) każdej linii. Na przykład, używając SHA-1, potrzebujesz tylko 20 bajtów plus stały narzut na linię. Ale przetwarzanie danych jest raczej powolne; ta metoda wygra tylko wtedy, gdy masz szybki procesor (zwłaszcza taki ze sprzętowym akceleratorem do obliczania skrótów) i nie ma dużo pamięci w stosunku do wielkości pliku i wystarczająco długich linii. Żadne podstawowe narzędzie nie pozwala obliczyć sumy kontrolnej dla każdej linii; będziesz musiał ponieść koszty interpretacji Perla / Pythona / Ruby /… lub napisać dedykowany skompilowany program.
źródło
awk '!seen[$0]++'
oznacza to, że jeśli awk zobaczy 2 zduplikowane linie, zachowa zawsze pierwszą linię i zignoruje wszystkie kolejne? (Czy zachowa ostatni?)sort -u
zmienia kolejność. Moja odpowiedź pokazuje rozwiązania, które zachowują porządek (a dokładniej kolejność pierwszych wystąpień).Zauważ, że plik wyjściowy zostanie posortowany.
źródło
awk
polecenie w innych odpowiedziach, ale koncepcyjnie proste!sort -u
do usuwania duplikatów podczas sortowania, a nie po nim. (I oszczędza przepustowość pamięci) przesyłając go do innego programu). Jest to lepsze niżawk
wersja, jeśli chcesz również posortować dane wyjściowe. (OP w tym pytaniu chce zachować jego pierwotne zamówienie , więc jest to dobra odpowiedź na nieco inny przypadek użycia).Zakładając, że możesz sobie pozwolić na zachowanie w pamięci nawet zduplikowanego pliku (jeśli twoje dane są rzeczywiście zduplikowane 100-krotnie, powinno to wynosić około 20MiB + narzut), możesz to zrobić bardzo łatwo za pomocą Perla.
To także zachowuje porządek.
Jeśli chcesz, możesz wyodrębnić liczbę wystąpień każdej linii z
%dup
skrótu, jako dodatkowy bonus.Jeśli wolisz
awk
, to też powinno to zrobić (ta sama logika co wersja perla, to samo porządkowanie, te same dane zebrane wdup
zmiennej):źródło
uniq
robi to samoPonieważ żadna inna odpowiedź nie zapewnia wsparcia w miejscu, oto jedna z nich:
źródło
GNU Awk 4.0.2
Możesz użyć
uniq
http://www.computerhope.com/unix/uuniq.htmuniq
zgłasza lub odfiltrowuje powtarzające się wiersze w pliku.źródło
'uniq' does not detect repeated lines unless they are adjacent.
Najpierw musisz go posortować i stracić kolejność niepowielonych linii.Wkładki Python One:
źródło
OrderedDict
Żadna z odpowiedzi tutaj nie działała na moim komputerze Mac, więc napisałem prosty skrypt Pythona, który działa dla mnie. Ignoruję początkowe / końcowe białe znaki, a także nie obchodzi mnie zużycie pamięci.
Zapisz powyższe w pliku Unique.py i uruchom w ten sposób:
źródło
W przypadku bash 4 można zastosować rozwiązanie typu pure bash, które wykorzystuje tablice asocjacyjne . Oto przykład
źródło
read
pętli do przetwarzania dużych plików tekstowych. bash musi czytać jeden bajt na raz, aby uniknąć przekroczenia nowej linii. Bash również nie jest bardzo szybki w przetwarzaniu tekstu w porównaniu do awk. Jeśli tego użyjesz,read -ra
unikniesz jedzenia odwrotnych ukośników w danych wejściowych. Nie zapomnij teżunset llist
po zakończeniu pętli, jeśli umieścisz ją w funkcji powłoki lub użyjesz jej interaktywnie.