it-swarm-eu.dev

Jak odstranit duplicitní řádky uvnitř textového souboru?

Obrovský (až 2 GiB) textový soubor obsahuje asi 100 přesných duplikátů každého řádku v něm (v mém případě zbytečný, protože jde o datovou tabulku podobnou CSV).

Potřebuji odstranit všechna opakování, zatímco (nejlépe, ale to lze obětovat za významné zvýšení výkonu) při zachování původního pořadí sekvencí. Ve výsledku musí být každý řádek jedinečný. Pokud by existovalo 100 stejných řádků (duplikáty jsou obvykle rozloženy po celém souboru a nebudou sousedé), zbývá pouze jeden druh.

Napsal jsem program v Scala (považujte jej za Java, pokud nevíte o Scale) implementovat to. Ale možná existují rychlejší nativní nástroje napsané v C, které to dokáží rychleji?

UPDATE: Zdá se, že řešení awk '!seen[$0]++' filename Pro mě fungovalo dobře, pokud byly soubory blízko 2 GiB nebo menší, ale nyní, když musím vyčistit 8 GiB souboru, to nefunguje Už nebudu pracovat. Zdá se, že nekonečno u Mac se 4 GiB RAM a 64-bitovým Windows 7 PC se 4 GiB RAM a 6 GiB swapem se právě vyčerpá paměti. A já se necítím nadšeně, že to vyzkouším na Linuxu, protože 4 GiB RAM vzhledem k této zkušenosti.

141
Ivan

Řešení awk vidět na #bash (Freenode):

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

Existuje jednoduchá (to neřeknutelná) metoda používající standardní obslužné programy, které nevyžadují velkou paměť kromě spuštění sort, která má ve většině implementací specifické optimalizace pro velké soubory (dobrý externí algoritmus řazení) . Výhodou této metody je to, že se točí pouze přes všechny řádky uvnitř speciálních utilit, nikdy uvnitř interpretovaných jazyků.

<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

Pokud všechny řádky začínají znakem bez mezer, můžete se vzdát některých možností:

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

Pro velké množství duplikace bude metoda, která vyžaduje pouze uložení jedné kopie každé řádky v paměti, fungovat lépe. S nějakou režijní interpretací existuje velmi stručný awk skript (již publikoval enzotib ):

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

Méně stručně: !seen[$0] {print} {seen[$0] += 1}, tzn. vytiskněte aktuální řádek, pokud ještě nebyl viděn, poté zvyšte čítač seen pro tento řádek (neinicializované proměnné nebo prvky pole mají číselnou hodnotu 0).

U dlouhých řádků můžete ušetřit paměť udržováním pouze kontrolního součtu, který není spoofable (např. Kryptografický výtah) každé řádky. Například při použití SHA-1 potřebujete pouze 20 bajtů plus konstantní režii na řádek. Počítačové digesce jsou ale poměrně pomalé; tato metoda vyhraje pouze v případě, že máte rychlý procesor (zejména procesor s hardwarovým akcelerátorem pro výpočet digescí) a málo paměti vzhledem k velikosti souboru a dostatečně dlouhým řádkům. Žádný základní obslužný program neumožňuje vypočítat kontrolní součet pro každý řádek; museli byste nést režijní náklady na Perl/Python/Ruby /… nebo napsat specializovaný kompilovaný program.

<input Perl -MDigest::MD5 -ne '$seen{Digest::MD5::md5($_)}++ or print' >output
sort -u big-csv-file.csv > duplicates-removed.csv

Výstupní soubor bude setříděn.

26

Za předpokladu, že si můžete dovolit ponechat v paměti tolik, jako je duplikovaný soubor (pokud jsou vaše data skutečně duplikována faktorem 100, což by mělo být asi 20 MB + režie), můžete to udělat velmi snadno pomocí Perlu.

$ Perl -ne 'print unless $dup{$_}++;' input_file > output_file

Tím se zachová i objednávka.

Mohli byste extrahovat počet výskytů každého řádku z %dup hash, pokud jste si to přáli, jako bonus navíc.

Pokud dáváte přednost awk, mělo by se to také provést (stejná logika jako verze Perlu, stejné řazení, stejná data shromážděná v proměnné dup):

$ awk '{if (++dup[$0] == 1) print $0;}' input_file > output_file
19
Mat

Protože žádná jiná odpověď neposkytuje podporu na místě, zde je jedna:

gawk -i inplace '!a[$0]++' file
7
rindeal

Můžete použít uniqhttp://www.computerhope.com/unix/uuniq.htm

uniq hlásí nebo filtruje opakované řádky v souboru.

3
Mahmoud Zalt

Python One vložky:

python -c "import sys; lines = sys.stdin.readlines(); print ''.join(sorted(set(lines)))" < InputFile
2
Rahul Patil

Žádná z odpovědí zde na mém počítači Mac nepracovala, takže jsem napsal jednoduchý skript python), který pro mě funguje. Ignoruji úvodní/koncové mezery a také se nestarám o spotřebu paměti.

import sys

inputfile = sys.argv[1]
outputfile = sys.argv[2]

with open(inputfile) as f:
    content = f.readlines()

content = [x.strip() for x in content]

my_list = list(set(content))

with open(outputfile, 'w') as output:
    for item in my_list:
        output.write("%s\n" % item)

Uložte výše na unique.py a spusťte takto:

python unique.py inputfile.txt outputfile.txt
0
Jared