Valtava (enintään 2 Gt) tekstitiedostoni sisältää noin 100 tarkkaa kopiota jokaisesta rivistä (minun tapauksessani turhaa, koska tiedosto on CSV: n kaltainen tietotaulukko).
Tarvitsen on poistaa kaikki toistot samalla kun (mieluiten, mutta tämä voidaan uhrata merkittävän suorituskyvyn parantamiseksi) säilyttäen alkuperäinen järjestysjärjestys. Tuloksessa jokaisen rivin on oltava ainutlaatuinen. Jos yhtäläisiä rivejä oli 100 (yleensä kaksoiskappaleet levitetään tiedostoon ja ne eivät ole naapureita), jäljellä on vain yksi laatu.
Olen kirjoittanut ohjelman Scalassa (pidä sitä Java, jos et tiedä Scalasta) tämän toteuttamiseksi. Mutta ehkä on nopeampia C-kirjoitettuja natiivityökaluja, jotka pystyvät tekemään tämän nopeammin?
PÄIVITYS: ratkaisu awk "!seen[$0]++" filename
näytti toimivan hienosti minulle niin kauan kuin tiedostot olivat lähellä 2 Gt tai pienempiä, mutta nyt kun puhdistan 8 Gt: n tiedoston, se ei toimi enää. Vaikuttaa siltä, että Macissa on 4 Gt RAM-muistia ja 64-bittisessä Windows 7 -tietokoneessa, jossa on 4 Gt RAM-muistia. ja 6 GiB -vaihdossa vain loppuu muisti. Enkä ole innostunut kokeilemasta sitä Linuxissa, jossa on 4 GiB RAM-muistia tämän kokemuksen vuoksi.
Kommentit
Vastaa
awk
-ratkaisu, joka näkyy #bash (Freenode):
awk "!seen[$0]++" filename
kommentit
- Kokeilin juuri tätä 2G-tiedostossa, ja muistikirjani kesti kolme minuuttia. Ei paha. Yritin myös uniq-tiedostonimeä awk ’! seen [$ 0] ++ ’, mutta sitä ei ollut ’ nopeammin.
- @HashWizard: tämä komento ei lajittele, mutta poistaa saman rivin jokaisen seuraavan esiintymisen.
- Mietitkö kuinka tämä komento toimii? – Katso täältä: unix.stackexchange.com/questions/159695/how-does-awk-a0-work
- @MaxWilliams kyllä , se toimii, jos ne jakautuvat satunnaisesti.
- säilytä uudet viivat tai välilyönnit
awk '/^\s*?$/||!seen[$0]++'
Vastaa
Siellä on yksinkertainen (ei kuitenkaan selvä) menetelmä, joka käyttää tavallisia apuohjelmia, jotka eivät vaadi suurta muistia lukuun ottamatta suoritusta sort
, jolla on useimmissa toteutuksissa erityisiä optimointeja valtaville tiedostoille (hyvä ulkoinen lajittelualgoritmi). Tämän menetelmän etuna on, että se silmukkaa vain kaikkien erityisapuohjelmien sisällä olevien viivojen yli, ei koskaan tulkittujen kielten sisällä.
<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
Jos kaikki rivit alkavat ei-välilyönti, voit luopua joistakin vaihtoehdoista:
<input nl | sort -k 2 -u | sort -k 1n | cut -f 2- >output
Suuren määrän päällekkäisyyksiä varten menetelmä, joka vaatii vain yhden kopion tallennuksesta kukin muistin rivi toimii paremmin. Jonkin verran tulkintaa ”, on olemassa hyvin ytimekäs awk-komentosarja ( lähettänyt enzotib ):
<input awk "!seen[$0]++"
Vähemmän ytimekkäästi: !seen[$0] {print} {seen[$0] += 1}
, eli tulosta nykyinen rivi, jos sitä ei ole vielä nähty, lisää sitten seen
laskuri tälle riville (alustamattomilla muuttujilla tai taulukkoelementeillä on numeroarvo 0).
Pitkillä viivoilla voit säästää muistia pitämällä jokaisen rivin vain tarkistamaton tarkistussumma (esim. salaus) . Esimerkiksi SHA-1: tä käytettäessä tarvitset vain 20 tavua plus vakiona yleiskustannukset per linja. Ruuansulatuksen laskeminen on kuitenkin melko hidasta; tämä menetelmä voittaa vain, jos sinulla on nopea keskusyksikkö (varsinkin sellainen, jossa on laitteistokiihdytin sulkujen laskemiseksi) eikä paljon muistia suhteessa tiedoston kokoon ja riittävän pitkiin viivoihin. Mikään perusapuohjelma ei anna sinun laskea tarkistussummaa jokaiselle riville; joudut kantamaan Perl / Python / Ruby /…: n tulkintakustannukset tai kirjoittamaan erillisen käännetyn ohjelman.
<input perl -MDigest::MD5 -ne "$seen{Digest::MD5::md5($_)}++ or print" >output
Kommentit
- @Gilles
awk '!seen[$0]++'
selityksesi perusteella tarkoittaako se sitä, että jos awk näkee kaksi päällekkäistä riviä, se pitää aina ensimmäisen ja jättää huomiotta kaikki seuraavia? (Tai säilyttääkö viimeisen?) - @ user779159 Se pitää ensimmäisen: jokainen syöttörivi joko tulostetaan välittömästi (ensimmäinen esiintyminen) tai ei ollenkaan (toistuva esiintyminen).
- Mutta miten tämä vertaa lajitteluun -u …?
- @HashWizard Tavallinen
sort -u
muuttaa järjestystä.Vastaukseni näyttää ratkaisuja, jotka säilyttävät järjestyksen (tarkemmin sanottuna ensimmäisten esiintymien järjestys). - @Gilles sanotko, että se on nopeampi kuin lajittelu -u suurille tiedostoille (10G), joissa on 50% kopioita ?
Vastaa
sort -u big-csv-file.csv > duplicates-removed.csv
Huomaa, että lähtötiedosto lajitella.
Kommentit
- Ei niin nopeasti kuin
awk
-komento muissa vastauksissa, mutta käsitteellisesti yksinkertainen! - @Johann Teen tämän melko usein tiedostoissa, joissa on satoja tuhansia (jopa miljoonia) lyhyitä uuden rivin päättyneitä merkkijonoja. Saan tulokset melko nopeasti tekemistäni kokeista. Se voi olla tärkeämpää, jos sitä käytetään yhä uudelleen suoritettavissa skripteissä, ajansäästö voi olla huomattavaa.
- Poista kaksoiskappaleet lajittelun avulla käyttämällä
sort -u
. pikemminkin kuin sen jälkeen. (Ja säästää muistin kaistanleveyttä) ohjaamalla se toiseen ohjelmaan). Tämä on vain parempi kuinawk
-versio, jos haluat myös tulosteesi lajitellun. (Tämän kysymyksen OP haluaa alkuperäisen tilauksensa säilyvän säilytettynä , joten tämä on hyvä vastaus hieman erilaiseen käyttötapaukseen.) - Kesti noin minuutti minulle 5,5 miljoonan rivitiedoston (yhteensä 1,8 Gt). Loistava.
Vastaa
Olettaen, että sinulla on varaa pitää yhtä paljon kuin kopioitu tiedosto muistissa ( Jos tietosi ovat todellakin päällekkäisiä kertoimella 100, sen pitäisi olla noin 20 Mt + yleiskustannukset), voit tehdä tämän erittäin helposti Perlillä.
$ perl -ne "print unless $dup{$_}++;" input_file > output_file
Tämä säilyttää myös tilauksen.
Voit halutessasi poimia kunkin rivin esiintymämäärän %dup
-räsitunnistuksesta lisäbonuksena.
Jos haluat mieluummin awk
, myös tämän pitäisi tehdä se (sama logiikka kuin perl-versio, sama järjestys, samat tiedot, jotka on kerätty dup
muuttuja):
$ awk "{if (++dup[$0] == 1) print $0;}" input_file > output_file
kommentit
- Tämä on liian hyvä @Mat, I aikoi ryöstää tiedostoa, lol ;-).
- Odottaa nyt myös @ManAtWorkia hänen sed- ja awk-taikakudoksilleen 🙂
- jälleen mahtava awk-kärjelle: – )
- Voiko perl-komentosarjan muuttaa vain poistettavaksi? e kopioida vierekkäisiä viivoja?
- @dumbledad:
uniq
tekee kaiken yksin
Vastaa
Koska muuta vastausta ei annettu paikan päällä, tässä on yksi:
gawk -i inplace "!a[$0]++" file
kommentit
- Säilyttääkö tämä järjestyksen? Muuten, tämä ei toiminut minulle. Oma versioni on:
GNU Awk 4.0.2
- @Leonid kyllä, kyllä. Se tulostaa minkä tahansa ainutlaatuisen viivan ensimmäisen esiintymisen. Paikallinen tuki otettiin ensimmäisen kerran käyttöön versiossa 4.1, joka julkaistiin vuonna 2013.
- Tämän pitäisi olla vastaus. Se ’ poistaa oikeastaan olemassa olevan tai nykyisen tiedoston päällekkäisen merkkijonon, jossa ylin vastaus ja suurin osa vastauksista vain tulostaa uniq / päällekkäiset merkkijonot ja tekemättä mitään, ja meidän on luotava toinen lähtö tuloksen tallentamiseksi.
Vastaa
Voit käyttää uniq
http://www.computerhope.com/unix/uuniq.htm
uniq
raportoi tai suodattaa tiedostossa toistuvat rivit.
Kommentit
- Vastausta annettaessa on suositeltavaa antaa selitys MIKSI vastauksesi on . Joten miten tämä vastaus eroaa useista edellisistä vastauksista?
- uniq-man -sivulta: Huomaa:
'uniq' does not detect repeated lines unless they are adjacent.
Joten sinun on ensin lajiteltava se ja irrotettava muiden kuin päällekkäisten rivien järjestys.
Vastaa
Python One -vuoret:
python -c "import sys; lines = sys.stdin.readlines(); print "".join(sorted(set(lines)))" < InputFile
kommentit
- tämä aiheuttaa koko tiedoston sekoittamisen muistiin, eikä se välttämättä sovi hyvin OP ’ -ongelmaan. Ei myöskään taata järjestyksen säilyttämistä
- Kiitos ehdotuksesta, olen ’ oppinut vain pythonia … kokeilin tätä vain oppimistarkoituksiin ..:)
- Tässä ’ on Python 2.7 -versio, joka ei ole yhden linjan, mutta (ytimekkäästi) palauttaa ainutlaatuiset rivit järjestyksen säilyttämättä lataamatta koko tiedostoa muistiin tai luomalla yhtä jättimäistä merkkijonoa syötettäväksi tulostettavaksi
- Kiitos @ 1_CR Minulla on jotain opittavaa tänään 🙂
OrderedDict
vastaus
Mikään täällä olevista vastauksista ei toiminut minulle Mac-tietokoneellani, joten kirjoitin yksinkertaisen pythonin käsikirjoitus, joka toimii minulle. Jätän huomiotta tyhjät tyhjät tilat ja välitän myös muistin kulutuksesta.
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)
Tallenna yllä oleva yksilölliseksi.py ja aja näin:
python unique.py inputfile.txt outputfile.txt
Vastaa
RATKAISU ILMAN ALKUPERÄISEN JAKSOJÄRJESTELMÄN SÄILYTTÄMISTÄ
Tein sen seuraavalla koodilla.
sort duplicates.txt | uniq > noDuplicates.txt
sort
-komento lajittelee rivit aakkosjärjestyksessä ja komento uniq
poistaa kaksoiskappaleet.
HUOMAUTUS: miksi lajittelimme viivat ensin, uniq
ei tunnista päällekkäisiä viivoja, elleivät ne ole vierekkäisiä.
Kommentit
- Kysymys pyytää menetelmää (mieluiten ), joka ylläpitää syöttöjärjestystä; voisitko muokata vastaustasi siihen vastaamiseksi? Huomaa, että on olemassa olemassa olevia vastauksia, jotka käyttävät
sort
-toimintoa ja jotka ylläpitävät syöttöjärjestystä, ja yksi vastaus käyttäensort
ylläpitämättä syöttöjärjestystä, mutta tehokkaammin kuin siirtämällä viestiuniq
. - @StephenKitt Edited. Tarkastin muita vastauksia, mutta en voinut ’ löytää mitään vain peruskomennoilla. Kiitos palautteestasi.
- Annoin sinulle linkin vastaukseen, joka sisältää vain peruskomennot, itse asiassa vain yhden komennon,
sort -u
(joka on osa POSIX ) ;-). - @StephenKitt näin vastauksen. Minun on myös tapa käsitellä ongelmaa. Mitä haluat minun tekevän enemmän? Pitäisikö minun poistaa vastaus?
- Ei, älä poista vastausta; Halusin vain varmistaa, että olit tietoinen toisesta vastauksesta, koska sanoit, ettet “voinut ’ löytää mitään vain peruskomennoilla.
vastaus
Bash 4: llä puhdas-bash-ratkaisu, joka hyödyntää -yhdistelmäryhmät voidaan käyttää. Tässä on esimerkki
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
kommentit
- Don ’ t käytä
read
-silmukoita suurten tekstitiedostojen käsittelemiseen. bashin on luettava yksi tavu kerrallaan, jotta vältetään uuden rivin ylitys. Bash ei myöskään ole kovin nopea tekstinkäsittelyssä yleensä verrattuna awk: iin. Jos käytät tätä,read -ra
välttää syöksymättä takaisinviivoja syötteessäsi. Älä myöskään unohda ’ unohtaaunset llist
silmukan jälkeen , jos laitat tämän kuoritoimintoon tai käytä sitä vuorovaikutteisesti. - @PeterCordes, tai olet voinut vain viitata tähän 🙂
sort -u
on todennäköisesti nopeampi.