2015-08-18 16 views
6

In pratica sto provando a leggere un file di grandi dimensioni (circa 10G) in un elenco di righe. Il file contiene una sequenza di numeri interi, qualcosa di simile:Leggere un file di grandi dimensioni in righe di stringa OCaml

0x123456 
0x123123 
0x123123 
..... 

ho usato il seguente metodo per leggere i file di default per la mia base di codice, ma si scopre di essere smettere di lenti (~ 12 minuti) in questo scenario

let lines_from_file (filename : string) : string list =                                                                              
    let lines = ref [] in                                            
let chan = open_in filename in                                          
    try                                                 
     while true; do                                              
     lines := input_line chan :: !lines                                        
    done; []                                               
    with End_of_file ->                                             
    close_in chan;                                              
    List.rev !lines;;   

Credo di aver bisogno di leggere il file in memoria e quindi dividerlo in righe (sto usando un server 128G, quindi dovrebbe andare bene per lo spazio di memoria). Ma non ho ancora capito se OCaml fornisce tale funzione dopo aver cercato i documenti here.

Quindi ecco la mia domanda:

  1. Data la mia situazione, come leggere i file in elenco di stringhe in modo veloce?

  2. Che ne dici di utilizzare stream? Ma ho bisogno di aggiustare il relativo codice dell'applicazione, quindi ciò potrebbe causare un po 'di tempo.

+0

Perché non stampare le linee in un nuovo file? In ordine inverso, una riga alla volta. –

risposta

7

Prima di tutto è necessario considerare se è davvero necessario avere tutte le informazioni in una sola volta nella memoria. Forse è meglio elaborare il file riga per riga?

Se si desidera veramente avere tutto in una volta in memoria, è possibile utilizzare la funzione Bigarray di map_file per mappare un file come una matrice di caratteri. E poi fai qualcosa con esso.

Inoltre, come vedo, questo file contiene numeri. Forse è meglio allocare l'array (o anche meglio un bigarray) e processare ogni riga in ordine e memorizzare interi nella matrice (grande).

+0

Qual è la situazione dello streaming in ocaml? Il flusso di Stdlib è considerato deprecato? –

+0

Sì, è sul bordo. In realtà è stato deprecato alla prima versione di OCaml, poiché è un atavismo di Caml Light. Altrimenti, lo streaming è ok. Puoi usare 'Lwt_stream', i pipe di Async, Core's' Sequence' o 'enum' delle batterie per creare sequenze o flussi di caratteri/stringhe. Ma questa è solo una versione meno generale di "In_channel.fold_lines' della libreria Core. – ivg

+0

se è sostanzialmente deprecato, allora perché non è appena rimosso dallo stdlib. –

0

Questo dovrebbe funzionare:

let rec ints_from_file fdesc = 
    try 
    let l = input_line fdesc in 
    let l' = int_of_string l in 
    l' :: ints_from_file fdesc 
    with | _ -> [] 

Questa soluzione converte le stringhe in numeri interi come sono stati letti (che dovrebbe essere un po 'più efficiente della memoria, e presumo che questo stava per essere fatto per loro alla fine .

Inoltre, perché è ricorsivo, il file deve essere aperto al di fuori della chiamata di funzione.

+1

Quella funzione non è ricorsiva in coda, quindi su un file di grandi dimensioni produrrà uno stack overflow. Usare un accumulatore per terminare con 'List.rev' sarebbe molto più consigliabile. – PatJ

+2

E anche con l'accumulatore, lo stack crescerà linearmente fino alla dimensione del file, a causa di una sezione 'try/with'. La soluzione di @alifirat è abbastanza idiomatica. – ivg

2

io uso spesso i due seguente funzione per leggere le righe di un file. si noti che la funzione è lines_from_files di coda ricorsivo

let read_line i = try Some (input_line i) with End_of_file -> None 

let lines_from_files filename = 
    let rec lines_from_files_aux i acc = match (read_line i) with 
    | None -> List.rev acc 
    | Some s -> lines_from_files_aux i (s :: acc) in 
    lines_from_files_aux (open_in filename) [] 

let() = 
    lines_from_files "foo" 
    |> List.iter (Printf.printf "lines = %s\n")