Hogyan lehet eltávolítani az ismétlődő sorokat egy szövegfájlban?

Egy hatalmas (legfeljebb 2 GiB méretű) szöveges fájlom minden sor kb. 100 pontos másolatát tartalmazza (esetemben haszontalan, mivel a fájl CSV-szerű adattábla).

Szükségem van az összes ismétlés eltávolítására, miközben (lehetőleg, de ez feláldozható a teljesítmény jelentős növelése érdekében) az eredeti sorrend megtartása. Ennek eredményeként minden sornak egyedinek kell lennie. Ha 100 egyforma sor volt (általában a duplikátumok el vannak oszlatva a fájlban, és nem lesznek szomszédok), akkor csak egy maradhat.

Scalában írtam egy programot (vegye fontolóra Java, ha nem tudsz a Scaláról) ennek megvalósításához. De lehet, hogy vannak gyorsabb C-írású natív eszközök, amelyek képesek erre gyorsabban?

UPDATE: a awk "!seen[$0]++" filename megoldás számomra tökéletesen működni látszott, amíg a fájlok 2 GiB vagy annál kisebb méretűek voltak, de most, amikor egy 8 GiB fájlt megtisztítok, ez már nem fog működni. Úgy tűnik, végtelenbe kerül egy 4 GiB RAM-mal rendelkező 64 bites és 4 GiB RAM-mal rendelkező 64 bites Windows 7 számítógépen és a 6 GiB swap-on éppen elfogy a memória. És nem vagyok lelkes, hogy 4 GiB RAM-mal próbálom kipróbálni Linuxon, tekintettel erre az élményre.

Hozzászólások

  • ez tönkreteszi a sorrendjét, de ha megpróbálta a sortu rendszert, akkor fogalmam sincs, hogyan és hogyan tud ilyen hatalmas fájlon futtatni
  • A C gyakran nem lényegesen gyorsabb, mint a Java, és ha ‘ most futtatod (sorrendben), ott ‘ jó esély van rá ‘ befejezem, mielőtt itt választ kapnál, hajtsd végre, és befejezi a futást; nem működik, a sort -u valószínűleg gyorsabb lesz.

Válasz

A awk megoldás látható a #bash (Freenode) oldalon:

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

Megjegyzések

  • Csak kipróbáltam egy 2G fájlon, és három percbe telt a noteszgépemen. Nem rossz. Kipróbáltam az uniq fájlnevet is awk ‘! seen [$ 0] ++ ‘, de nem volt ‘ gyorsabb.
  • @HashWizard: ez a parancs nem rendezi, hanem kiküszöböli ugyanazon sor minden következő előfordulását
  • Kíváncsi vagy, hogy működik ez a parancs? – Lásd itt: unix.stackexchange.com/questions/159695/how-does-awk-a0-work
  • @MaxWilliams igen , működik, véletlenszerűen vannak elosztva.
  • új sorok vagy szóközökkel rendelkező sorok megőrzése awk '/^\s*?$/||!seen[$0]++'

Válasz

Van egy egyszerű (ami nem mondható magától értetődő) módszer a normál segédprogramok használatával, amelyekhez nincs szükség nagy memóriára, csak a sort, amely a legtöbb implementációban speciális optimalizálásokkal rendelkezik a hatalmas fájlok számára (jó külső rendezési algoritmus). Ennek a módszernek az az előnye, hogy csak a speciális rendeltetésű segédprogramokban lévő összes vonalon hurkol, soha nem az értelmezett nyelveken.

<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 

Ha minden sor egy nem fehér szóköz, eltekinthet a következő lehetőségek közül:

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

Nagy mennyiségű másolás esetén olyan módszer, amely csak a a memória minden sora jobban fog teljesíteni. Némi értelmezési rezsivel “nagyon tömör awk szkript van ehhez (már írta: enzotib ):

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

Kevésbé tömören: !seen[$0] {print} {seen[$0] += 1}, azaz nyomtassa ki az aktuális sort, ha még nem volt látható, majd növelje a seen ennek a sornak a számlálója (az inicializálatlan változók vagy tömbelemek számértéke 0).

Hosszú sorok esetén memóriát takaríthat meg, ha az egyes sorokból csak egy nem spoofolható ellenőrző összeget (pl. kriptográfiai kivonatot) tart. . Például az SHA-1 használatával vonalonként csak 20 bájtra és állandó rezsire van szükség. Az emésztések kiszámítása azonban meglehetősen lassú; ez a módszer csak akkor nyer, ha gyors CPU-ja van (főleg hardveres gyorsítóval rendelkezik az emésztések kiszámításához), és nincs sok memóriája a fájl méretéhez és a kellően hosszú sorokhoz képest. Egyetlen alapvető segédprogram sem teszi lehetővé az ellenőrző összeg kiszámítását az egyes sorokhoz; neked kell viselned a Perl / Python / Ruby /… értelmezési fejeit, vagy dedikált fordított programot kell írni.

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

Megjegyzések

  • @Gilles Az awk '!seen[$0]++' magyarázatod alapján azt jelenti-e, hogy ha az awk 2 duplikált sort lát, akkor az mindig megtartja az elsőt, és figyelmen kívül hagy mindent a későbbieket? (Vagy megtartja az utolsót?)
  • @ user779159 Megtartja az elsőt: minden beviteli sort vagy azonnal kinyomtatnak (első előfordulás), vagy egyáltalán nem nyomtatnak (ismétlődő előfordulás).
  • De hogy hasonlít ez az -u rendezéshez …?
  • @HashWizard Egy sima sort -u sorrendet változtat.Válaszom olyan megoldásokat mutat, amelyek megőrzik a sorrendet (pontosabban az első előfordulások sorrendjét).
  • @Gilles azt mondaná, hogy gyorsabb, mint az -u a nagy fájlok (10G) esetén, 50% duplikátummal ?

Válasz

sort -u big-csv-file.csv > duplicates-removed.csv 

Ne feledje, hogy a kimeneti fájl rendezni kell.

Megjegyzések

  • Nem olyan gyorsan, mint a awk parancs más válaszokban, de fogalmilag egyszerű!
  • @Johann Ezt elég gyakran csinálom olyan fájlokon, amelyeken több százezer (akár millió) rövid új vonallal lezárt karakterlánc található. Gyorsan megkapom az eredményeket az általam végzett kísérletekhez. Sokkal fontosabb lehet, ha újra és újra futtatott szkriptekben használják, az időmegtakarítás jelentős lehet.
  • Használja az sort -u parancsot a másolatok eltávolításához a rendezés során, nem pedig utána. (És spórolja a memória sávszélességét) átirányítja egy másik programhoz). Ez csak akkor jobb, mint a awk verzió, ha a kimenetet is rendezni szeretné. (A kérdéssel foglalkozó OP azt akarja, hogy az eredeti rendje megmaradjon , ezért ez jó válasz egy kissé eltérő felhasználási esetre.)
  • Kb. Egy percet vett igénybe, nekem egy 5,5 millió sorfájl (összesen 1,8 GB). Zseniális.

Válasz

Feltéve, hogy megengedheti magának, hogy annyit tartson a memóriában a duplikált fájlokból ( ha az adatait valóban megduplázza 100-szoros, ennek kb. 20MiB + általános költségnek kell lennie, akkor ezt nagyon egyszerűen megteheti a Perl-lel.

$ perl -ne "print unless $dup{$_}++;" input_file > output_file 

Ez megőrzi a sorrendet is.

Ha szeretné, kivonhatja az egyes sorok előfordulásának számát a %dup hash-ból, további bónuszként.

Ha a awk -t részesíti előnyben, akkor ezt is meg kell tennie (ugyanaz a logika, mint a perl verzió, ugyanaz a sorrend, ugyanazok az adatok gyűjtöttek a dup változó):

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

Megjegyzések

  • Ez túl jó @Mat, I a fájl durranására készült, lol ;-).
  • Mostantól várja a @ManAtWork-ot az ő sed és awk varázsszöveteire is 🙂
  • ismét fantasztikus az awk tippje számára: – )
  • Meg lehet-e változtatni a perl szkriptet csak eltávolításra Megismétli a szomszédos vonalakat?
  • @dumbledad: uniq ezt önmagában csinálja

Válasz

Mivel más válasz nem adott helyben támogatást, itt van egy:

gawk -i inplace "!a[$0]++" file 

Megjegyzések

  • Megőrzi ez a rendet? Egyébként nekem ez nem sikerült. Az én verzióm: GNU Awk 4.0.2
  • @Leonid igen, igen. Kiírja minden egyedi sor első előfordulását. A helyszíni támogatást először a 4.1-es verzióban vezették be, amely 2013-ban jelent meg.
  • Erre kell válaszolni. ‘ valójában törli a duplikált karakterláncot a meglévő vagy aktuális fájlból, ahol a legfelsőbb válasz és a legtöbb válasz itt csak az uniq / duplikált karakterláncokat nyomtatja ki, és nem csinál semmit, és létre kell hoznunk másik kimenet az eredmény tárolásához.

Válasz

Használhatja a uniq http://www.computerhope.com/unix/uuniq.htm

uniq jelentést készít vagy kiszűri a fájlban az ismétlődő sorokat.

Megjegyzések

  • Válasz megadásakor célszerűbb megadni néhány magyarázat arra, hogy MIÉRT válaszol ? Tehát, miben különbözik ez a válasz az előző válaszok többitől?
  • Az uniq man oldalon: Megjegyzés: 'uniq' does not detect repeated lines unless they are adjacent. Tehát először rendeznie kell, és el kell engednie a nem duplikált sorok sorrendje.

Válasz

Python One bélések:

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

Megjegyzések

  • emiatt a teljes fájl a memóriába süllyed, és nem biztos, hogy megfelel az OP ‘ problémának. Ugyancsak nem garantált a rend megtartása
  • Köszönöm a javaslatot, én ‘ csak python-t tanultam .. ezt csak tanulási céllal próbáltam ..:)
  • Itt ‘ s egy Python 2.7 verzió, amely nem egyvonalas, hanem (tömören) egyedi sorokat ad vissza, amelyek megőrzik a sorrendet anélkül, hogy betöltenék a teljes fájlt a memóriába, vagy egyetlen óriási karakterláncot hoznának létre a nyomtatáshoz. div>

Válasz

Az itt felsorolt válaszok egyike sem működött nekem a Mac gépemen, ezért írtam egy egyszerű pitont nekem megfelelő forgatókönyv. Figyelmen kívül hagyom a vezető / záró szóközöket, és a memóriafogyasztás sem érdekel.

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) 

A fentieket mentse egyedinek.py és fuss így:

python unique.py inputfile.txt outputfile.txt 

Válasz

MEGOLDÁS AZ EREDETI SZekvencia Rendelés fenntartása nélkül

A következő kóddarabbal csináltam.

sort duplicates.txt | uniq > noDuplicates.txt 

A sort parancs ábécésorrendbe rendezi a sorokat, az uniq parancs pedig eltávolítja az ismétlődéseket.

MEGJEGYZÉS: Miért rendeztük először a sorokat, az az, hogy uniq csak párhuzamos sorokat észlel, csak ha szomszédosak.

Megjegyzések

  • A kérdés metódust kér (lehetőleg ), amely fenntartja a bemeneti sorrendet; szerkesztheti válaszát, hogy ezt megszólítsa? Ne feledje, hogy léteznek olyan válaszok, amelyek sort -et használnak, amelyek fenntartják a bemeneti sorrendet, és egy válasz a a beviteli sorrend fenntartása nélkül, de hatékonyabban, mint a uniq felé vezetés.
  • @StephenKitt szerkesztve. Vizsgáltam más válaszokat, de nem tudtam ‘ csak az alapvető parancsokkal találni semmit. Köszönjük visszajelzését.
  • linket adtam egy válaszra, amely csak alapvető parancsokat tartalmaz, valójában csak egy parancsot, sort -u (amely a POSIX része) ;-).
  • @StephenKitt láttam ezt a választ. Az enyém is a probléma kezelésének egyik módja. Mit akarsz, hogy tegyek többet? Törlöm a választ?
  • Nem, ne törölje a választ; Csak arról szerettem volna megbizonyosodni, hogy tisztában van-e a másik válasszal, tekintettel arra, hogy azt mondta, hogy „nem találhat semmit csak az alapvető parancsokkal”.

Válasz

A bash 4 segítségével egy tiszta-bash megoldás, amely kihasználja az asszociatív tömbök előnyeit használható. Íme egy példa

unset llist; declare -A llist; while read -r line; do if [[ ${llist[$line]} ]]; then continue else printf "%s\n" "$line" llist[$line]="x" fi done < file.txt 

Megjegyzések

  • Don ‘ t a read hurkokkal használja a nagy szöveges fájlok feldolgozását. bash-nak egyenként kell bájtot olvasnia, hogy elkerülje az új sor túllépését. A Bash általában nem túl gyors a szövegfeldolgozásban az awk-hoz képest. Ha mégis ezt használja, akkor a read -ra elkerüli a visszavágás megevését a bevitelben. Ne felejtsen el ‘ elfelejteni unset llist a hurok után is, ha ezt egy shell függvénybe helyezi, vagy használja interaktívan.
  • @PeterCordes, különben hivatkozhatott volna erre erre: 🙂

Vélemény, hozzászólás?

Az email címet nem tesszük közzé. A kötelező mezőket * karakterrel jelöltük