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
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 aawk
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 auniq
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 aread -ra
elkerüli a visszavágás megevését a bevitelben. Ne felejtsen el ‘ elfelejteniunset 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: 🙂
sort -u
valószínűleg gyorsabb lesz.