c ++ - Indeksering Kæmpe tekstfil

Indlæg af Hanne Mølgaard Plasc

Problem



Jeg har en stor tekstfil (over 100 gigs) med 6 kolonner af data (faneblad som separator). I første kolonne har jeg heltalsværdi (2500 forskellige værdier i sæt). Jeg skal opdele denne fil i flere mindre filer afhængigt af værdien i første kolonne (bemærk at rækker ikke er sorteret). Hver af disse mindre filer vil blive brugt til at forberede plot i matlab.


Jeg har kun 8 GB ram.


Problemet er, hvordan man gør det effektivt? Nogle ideer?

Bedste reference


Brug af bash:


cat 100gigfile | while read line; do
  intval="$( echo "$line" | cut -f 1)"
  chunkfile="$( printf '\%010u.txt' "$intval" )"
  echo "$line" >> "$chunkfile"
done


Det vil opdele din 100 gig-fil i (som du siger) 2500 individuelle filer opkaldt efter værdien af ​​det første felt. Du skal muligvis justere formatargumentet til printf efter din smag.

Andre referencer 1


one-liner med bash + awk:


awk '{print $0 >> $1".dat" }' 100gigfile 


Dette vil tilføre hver linje i din store fil til en fil, der hedder den første kolonne 's værdi +' .dat 'udvidelse, for eksempel linje 12 aa bb cc dd ee ff, vil gå til filen 12.dat.

Andre referencer 2


For Linux 64 bit (jeg er ikke sikker på om det virker til Windows), kan du mmap filen og kopiere blokke til nye filer. Jeg tror, ​​at dette ville være den mest effektive måde at gøre det på.

Andre referencer 3


Den mest effektive måde bliver blok for blok, åbner alle filer på en gang og genbruger læserabufferen til skrivning. Da oplysningerne gives, er der ikke noget andet mønster i de data, der kunne bruges til at fremskynde det.


Du åbner hver fil i en anden filbeskrivelse for at undgå åbning og lukning med hver linje. Åbn dem alle i begyndelsen eller lat som du går. Luk dem alle, inden du slutter. De fleste Linux-distributioner tillader kun 1024 åbne filer som standard, så du bliver nødt til at sætte grænsen op, siger brug af ulimit -n 2600, da du har tilladelse til at gøre det (se også /etc/security/limits.conf).


Allokere en buffer, sig et par kb, og rå læse fra kildefilen til den. Iterate dog og holde kontrolvariabler. Når du når en endelinje eller enden af ​​bufferen, skriv fra bufferen til den korrekte filbeskrivelse. Der er et par kantsager du skal tage i betragtning, som når en læsning får en ny linje, men ikke nok til at finde ud af, hvilken fil der skal gå i.


Du kan reverse-iterate for at undgå at behandle de første par byte af bufferen, hvis du regner med minimale linjestørrelse. Dette vil vise sig at være lidt vanskeligere, men en fremskyndelse alligevel.


Jeg spekulerer på, om ikke-blokerende I/O tager sig af problemer som denne.

Andre referencer 4


Den indlysende løsning er at åbne en ny fil hver gang du møder en ny værdi, og hold den åben indtil slutningen. Men dit OS muligvis ikke tillader dig at åbne 2500 filer på én gang. Så hvis du kun skal gøre dette en gang, kan du gøre det på denne måde:



  1. Gå gennem filen, bygg en liste over alle værdier. Sorter denne liste. (Du behøver ikke dette trin, hvis du på forhånd ved hvad værdierne vil være.)

  2. Indstil StartIndex til 0.

  3. Åbn, siger 100 filer (uanset hvilket operativsystem du har brug for). Disse svarer til de næste 100 værdier i listen, fra list[StartIndex] til list[StartIndex+99].

  4. Gå gennem filen ved at udføre disse poster med list[StartIndex] <= value <= list[StartIndex+99].

  5. Luk alle filerne.

  6. Tilføj 100 til StartIndex, og gå til trin 3, hvis du ikke er færdig.



Så du har brug for 26 passerer gennem filen.

Andre referencer 5


I din skal ...


$ split -d -l <some number of lines> Foo Foo


Det vil opdele en stor fil Foo til Foo1 gennem FooN, hvor n bestemmes af antallet af linjer i originalen divideret med den værdi, du leverer til -l. Iterate over stykkerne i en loop ...


REDIGER ... godt punkt i kommentaren ... dette script (nedenfor) vil læse linje for linje, klassificere og tildele til en fil baseret på det første felt ...


#!/usr/bin/env python
import csv

prefix = 'filename'
reader = csv.reader(open('\%s.csv' \% prefix, 'r'))
suffix = 0
files = {}
# read one row at a time, classify on first field, and send to a file
# row[0] assumes csv reader does *not* split the line... if you make it do so,
# remove the [0] indexing (and strip()s) below
for row in reader:
    tmp = row[0].split('	')
    fh = files.get(tmp[0].strip(), False)
    if not fh:
        fh = open('\%s\%05i.csv' \% (prefix, suffix), 'a')
        files[tmp[0].strip()] = fh
        suffix += 1
    fh.write(row[0])

for key in files.keys():
    files[key].close()