2012-05-10 11 views
6

Ho un file ASCII in cui ogni riga contiene un record di lunghezza variabile. Ad esempiolettura e scrittura in blocchi su linux utilizzando c

Record-1:15 characters 
Record-2:200 characters 
Record-3:500 characters 
... 
... 
Record-n: X characters 

Poiché le dimensioni del file sono circa 10 GB, vorrei leggere il record in blocchi. Una volta letti, ho bisogno di trasformarli, scriverli in un altro file in formato binario.

Così, per la lettura, la mia prima reazione è stata di creare un array di caratteri, come

FILE *stream; 
char buffer[104857600]; //100 MB char array 
fread(buffer, sizeof(buffer), 104857600, stream); 
  1. È corretto presumere, che Linux emetterà una chiamata di sistema e recuperare l'intero 100MB?
  2. Poiché i record sono separati da una nuova riga, cerco carattere per carattere per un nuovo carattere di linea nel buffer e ricostruisco ciascun record.

La mia domanda è che è così che dovrei leggere in blocchi o c'è un'alternativa migliore per leggere i dati in blocchi e ricostituire ogni record? C'è un modo alternativo per leggere il numero x di linee di dimensioni variabili da un file ASCII in una chiamata?

Avanti durante la scrittura, faccio lo stesso. Ho un buffer char di scrittura, che passo a fwrite per scrivere un intero set di record in una sola chiamata.

fwrite(buffer, sizeof(buffer), 104857600, stream); 

UPDATE: Se i setbuf (flusso, buffer), dove buffer è il mio char buffer di 100 MB, sarebbe fgets ritorno dal buffer o causare un disco IO?

+0

Cerca in fget, otterrà una riga alla volta per te se desideri. –

+0

Vorrei evitare di leggere riga per riga, ma preferirei leggere linee X di dimensioni variabili in un colpo solo. Inoltre, con fgets(), avrei bisogno di un buffer nel quale si inserisse la riga più lunga. Poiché le dimensioni del mio record possono variare da pochi 100 byte a 16 MB, perderei memoria. – Jimm

risposta

6
  1. Sì, fread preleverà l'intera cosa in una sola volta. (Supponendo che sia un file normale.) Ma non leggerà 105 MB a meno che il file stesso non sia 105 MB, e se non controlli il valore di ritorno non hai modo di sapere quanti dati sono stati effettivamente letti, o se ci sono era un errore

  2. Usa fgets (vedi man fgets) al posto di fread. Questo cercherà le interruzioni di riga per te.

    char linebuf[1000]; 
    FILE *file = ...; 
    while (fgets(linebuf, sizeof(linebuf), file) { 
        // decode one line 
    } 
    
  3. C'è un problema con il codice.

    char buffer[104857600]; // too big 
    

    Se si tenta di allocare un buffer di grandi dimensioni (105 MB è certamente grande) sullo stack, allora fallirà e il programma andrà in crash. Se hai bisogno di un buffer così grande, dovrai allocarlo sullo heap con malloc o simile. Sicuramente manterrei l'utilizzo dello stack per una singola funzione tra decine di KB al massimo, anche se probabilmente si potrebbero ottenere pochi MB sulla maggior parte dei sistemi Linux di serie.

In alternativa, è possibile solo mmap l'intero file in memoria. Questo non migliorerà o peggiorerà le prestazioni nella maggior parte dei casi, ma è più facile lavorare con.

int r, fdes; 
struct stat st; 
void *ptr; 
size_t sz; 

fdes = open(filename, O_RDONLY); 
if (fdes < 0) abort(); 
r = fstat(fdes, &st); 
if (r) abort(); 
if (st.st_size > (size_t) -1) abort(); // too big to map 
sz = st.st_size; 
ptr = mmap(NULL, sz, PROT_READ, MAP_SHARED, fdes, 0); 
if (ptr == MAP_FAILED) abort(); 
close(fdes); // file no longer needed 

// now, ptr has the data, sz has the data length 
// you can use ordinary string functions 

Il vantaggio di utilizzare mmap è che il vostro programma non esaurire la memoria. Su un sistema a 64 bit, puoi mettere l'intero file nello spazio degli indirizzi allo stesso tempo (anche un file da 10 GB) e il sistema leggerà automaticamente i nuovi blocchi quando il tuo programma accede alla memoria.I vecchi blocchi verranno automaticamente scartati e riletti se il tuo programma ne ha bisogno.

È un modo molto carino per arare file di grandi dimensioni.

0

la mia opinione utilizza fgets(buff) per rilevare automaticamente la nuova linea.

e quindi utilizzare strlen(buff) per contare la dimensione del buffer,

if((total+strlen(buff)) > 104857600) 

quindi scrivere nel nuovo pezzo ..

Ma le dimensioni del pezzo sarà difficilmente 104857600 byte.

CMIIW

+0

Nel mio caso il buff è un array di caratteri. Ma la documentazione di Fgets afferma che opera su un flusso di tipo FILE. – Jimm

+0

[fget] (http://www.cplusplus.com/reference/clibrary/cstdio/fgets/) ci sono 3 parametri 'char *', 'int' e' FILE * '. Quindi per il tuo caso metti il ​​tuo buffer su 'char *' –

2

Se è possibile, si potrebbe scoprire che mmap ING il file sarà più facile. mmap esegue il mapping di una (porzione di un) file in memoria in modo che sia possibile accedere all'intero file essenzialmente come una matrice di byte. Nel tuo caso, potrebbe non essere in grado di mappare l'intero file in una volta si sarebbe simile:

#include <stdio.h> 
#include <sys/stat.h> 
#include <sys/types.h> 
#include <unistd.h> 
#include <sys/mman.h> 


/* ... */ 

struct stat stat_buf; 
long pagesz = sysconf(_SC_PAGESIZE); 
int fd = fileno(stream); 
off_t line_start = 0; 
char *file_chunk = NULL; 
char *input_line; 
off_t cur_off = 0; 
off_t map_offset = 0; 
/* map 16M plus pagesize to ensure any record <= 16M will always fit in the mapped area */ 
size_t map_size = 16*1024*1024+pagesz; 
if (map_offset + map_size > stat_buf.st_size) { 
    map_size = stat_buf.st_size - map_offset; 
} 
fstat(fd, &stat_buf); 
/* map the first chunk of the file */ 
file_chunk = mmap(NULL, map_size, PROT_READ, MAP_SHARED, fd, map_offset); 
// until we reach the end of the file 
while (cur_off < stat_buf.st_size) { 
    /* check if we're about to read outside the current chunk */ 
    if (!(cur_off-map_offset < map_size)) { 
    // destroy the previous mapping 
    munmap(file_chunk, map_size); 
    // round down to the page before line_start 
    map_offset = (line_start/pagesz)*pagesz; 
    // limit mapped region to size of file 
    if (map_offset + map_size > stat_buf.st_size) { 
     map_size = stat_buf.st_size - map_offset; 
    } 
    // map the next chunk 
    file_chunk = mmap(NULL, map_size, PROT_READ, MAP_SHARED, fd, map_offset); 
    // adjust the line start for the new mapping 
    input_line = &file_chunk[line_start-map_offset]; 
    } 
    if (file_chunk[cur_off-map_offset] == '\n') { 
    // found a new line, process the current line 
    process_line(input_line, cur_off-line_start); 
    // set up for the next one 
    line_start = cur_off+1; 
    input_line = &file_chunk[line_start-map_offset]; 
    } 
    cur_off++; 
} 

La maggior parte della complicazione è quello di evitare di fare troppo grande una mappatura. Potresti essere in grado di mappare l'intero file utilizzando

char *file_data = mmap(NULL, stat_buf.st_size, PROT_READ, MAP_SHARED, fd, 0); 
Problemi correlati