2015-08-18 33 views
6

Zasadniczo próbuję odczytać duży plik (około 10G) na listę linii. Plik zawiera sekwencję liczb całkowitych, coś takiego:Przeczytaj duży plik na liniach ciągowych OCaml

0x123456 
0x123123 
0x123123 
..... 

Użyłem poniżej metody do odczytu plików domyślnie moim kodzie, ale okazuje się, że można rzucić SLOW (~ 12 minut) w tym scenariuszu

Zgaduję, że muszę odczytać plik do pamięci, a następnie podzielić go na linie (używam serwera 128G, więc powinno być dobrze dla miejsca w pamięci). Ale nadal nie rozumiałem, czy OCaml zapewnia taką możliwość po przeszukaniu dokumentów here.

Więc tutaj jest moje pytanie:

  1. Biorąc moja sytuacja, jak czytać pliki do listy ciąg w szybki sposób?

  2. Jak korzystać z stream? Ale muszę dostosować powiązany kod aplikacji, co może spowodować pewien czas.

+0

Dlaczego nie wydrukować linii w nowym pliku? W odwrotnej kolejności, po jednej linii na raz. –

Odpowiedz

7

Przede wszystkim należy rozważyć, czy naprawdę trzeba mieć wszystkie informacje naraz w pamięci. Może lepiej jest przetwarzać plik line-by-line?

Jeśli naprawdę chcesz mieć wszystko naraz w pamięci, możesz użyć funkcji Bigarray, aby zmapować plik jako tablicę znaków. A potem zrób coś z tym.

Ponadto, jak widzimy, plik ten zawiera liczby. Być może lepiej jest przydzielić tablicy (lub nawet lepiej bigarray) i proces każdego wiersza w kolejności i przechowywać liczby całkowite w (dużej) tablicy.

+0

Jaka jest sytuacja z przesyłaniem strumieniowym w ocaml? Czy strumień Stdliba jest uważany za przestarzały? –

+0

Tak, jest na krawędzi. Właściwie został wycofany z pierwszego wydania OCaml, ponieważ jest to atawizm Caml Light. W przeciwnym razie transmisja strumieniowa jest w porządku. Możesz użyć 'Lwt_stream', Async's pipes, Core' 'Sequence' lub batteries' enum', aby utworzyć sekwencje lub strumienie znaków/ciągów. Ale to wszystko jest po prostu mniej ogólną wersją 'In_channel.fold_lines' biblioteki Core. – ivg

+0

jeśli jest zasadniczo przestarzałe, to dlaczego nie jest po prostu usunięte ze stdlib. –

0

To powinno działać:

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 | _ -> [] 

To rozwiązanie zamienia ciągi liczb całkowitych jak oni czytają w (który powinien być nieco bardziej wydajne pamięci, i zakładam to miało być zrobione do nich ostatecznie .

Ponadto, ponieważ jest rekurencyjne, plik musi być otwarty na zewnątrz wywołania funkcji.

+1

Ta funkcja nie jest rekursywna, więc na dużym pliku spowoduje przepełnienie stosu. Używanie akumulatora, a następnie kończenie na "List.rev" byłoby o wiele bardziej wskazane. – PatJ

+2

Nawet z akumulatorem stos będzie nadal wzrastał liniowo do rozmiaru pliku, ze względu na sekcję 'try/with'. Rozwiązanie @ alifirat jest dość idiomatyczne. – ivg

2

często używam dwóch następujących funkcji czytać wiersze pliku. należy zauważyć, że funkcja lines_from_files jest tail- rekurencyjny .

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")