2011-04-25 35 views

回答

17

如果您沒有安裝Extlib(顯然你不基於上述錯誤信息)做的話,一般它的完成是這樣的:

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

如果你確實有EXTLIB:

let read_file filename = 
    let chan = open_in filename in 
    Std.input_list chan 

...這是相當多的,你有什麼。

如果你有Batteries-included庫,你可以讀入文件到Enum.t和迭代,如下所示:

let filelines = File.lines_of filename in 
Enum.iter (fun line -> (*Do something with line here*)) filelines 
+0

非常感謝! – Travis 2011-04-25 20:12:05

+2

在extlib變體中檢測到fd泄漏:輸入通道未關閉 – ygrek 2012-11-21 16:08:27

+0

您能解釋爲什麼您將while循環鏈接爲空列表?我認爲它永遠不會達成。 – Rizo 2015-05-08 16:32:31

0

Std.input_list顯然需要Extlib,你應該安裝系統(libextlib-ocamllibextlib-ocaml-dev上在Debian系統上)。

1

下面是使用scanf函數的遞歸解決方案:

let read_all_lines file_name = 
    let in_channel = open_in file_name in 
    let rec read_recursive lines = 
    try 
     Scanf.fscanf in_channel "%[^\r\n]\n" (fun x -> read_recursive (x :: lines)) 
    with 
     End_of_file -> 
     lines in 
    let lines = read_recursive [] in 
    let _ = close_in_noerr in_channel in 
    List.rev (lines);; 

用法:

let all_lines = read_all_lines "input.txt";; 

不過,我寧願流線由行:

let make_reader file_name = 
    let in_channel = open_in file_name in 
    let closed = ref false in 
    let read_next_line = fun() -> 
    if !closed then 
     None 
    else 
     try 
     Some (Scanf.fscanf in_channel "%[^\r\n]\n" (fun x -> x)) 
     with 
     End_of_file -> 
      let _ = close_in_noerr in_channel in 
      let _ = closed := true in 
      None in 
    read_next_line;; 

用法:

let read_next = make_reader "input.txt";; 
let next_line = read_next();; 

而且可能有點結冰:

type reader = {read_next : unit -> string option};; 

let make_reader file_name = 
    let in_channel = open_in file_name in 
    let closed = ref false in 
    let read_next_line = fun() -> 
    if !closed then 
     None 
    else 
     try 
     Some (Scanf.fscanf in_channel "%[^\r\n]\n" (fun x -> x)) 
     with 
     End_of_file -> 
      let _ = close_in_noerr in_channel in 
      let _ = closed := true in 
      None in 
    {read_next = read_next_line};; 

用法:

let r = make_reader "input.txt";; 
let next_line = r.read_next();; 

希望這有助於!

+0

[http://caml.inria.fr/pub/docs/manual-ocaml/libref/Scanf.html#space](http://caml.inria.fr/pub/docs/manual-ocaml/libref/Scanf .html#space) _ ** ...類似地,格式字符串中的換行符匹配單個換行或回車後跟換行** _ 因此,此解決方案「應該」適用於「\ r \ n」和「\ n」樣式行結尾(僅在Linux系統上測試)。 – 2012-11-10 14:22:31

+0

所有代碼片段都不關閉輸入通道,因此泄漏文件描述符 – ygrek 2012-11-21 16:09:50

+0

@ygrek:謝謝!固定。可能最好讓主叫方開啓/關閉業務,並讓這些功能代替輸入頻道。 – 2012-11-21 16:47:45

0

使用Scanf「字符串指示」和零寬度字符從文件中讀取行的另一種樣式。這就像傳統的祈禱風格。

open Scanf 
open Printf 

(* little helper functions *) 
let id x = x 
let const x = fun _ -> x 
let read_line file = fscanf file "%[email protected]\n" id 
let is_eof file = try fscanf file "%0c" (const false) with End_of_file -> true 

let _ = 
    let file = open_in "/path/to/file" in 

    while not (is_eof file) do 
    let s = read_line file in 
    (* do something with s *) 
    printf "%s\n" s 
    done; 

    close_in file 

注:

  1. read_line忽略一個尾隨\ n,所以如果你的文件 的最後一個字符爲\ n,它可能好像你已經錯過了最後的空行。
  2. 當使用Scanf時,由於緩衝區的作用,不要在同一通道上混合其他低電平讀數,否則會導致奇怪的行爲。
15

如果您已經安裝了OCaml的核心庫,那麼很簡單:

open Core.Std 
let r file = In_channel.read_lines file 

如果您已經安裝corebuild,那麼你可以用它編譯代碼:

corebuild filename.byte 

如果您的代碼駐留在名爲filename.ml的文件中。

如果您沒有OCaml Core,或者不想安裝它或其他標準庫實現,那麼您當然可以使用vanilla OCaml的標準庫來實現它。在Pervasives模塊中定義了一個函數input_line,該函數在所有OCaml程序中自動打開(即,所有的定義都可以在沒有進一步說明的情況下使用模塊名稱進行訪問)。該函數接受in_channel類型的值並返回從通道讀取的一行。使用此功能,可以實現所需的功能:

let read_lines name : string list = 
    let ic = open_in name in 
    let try_read() = 
    try Some (input_line ic) with End_of_file -> None in 
    let rec loop acc = match try_read() with 
    | Some s -> loop (s :: acc) 
    | None -> close_in ic; List.rev acc in 
    loop [] 

此實現使用遞歸,並且更加自然的OCaml節目。

+0

感謝您的遞歸實現!我似乎瞭解大部分代碼,除了在循環[]中的'List.rev acc'外。你能解釋一下到底發生了什麼? – nambvarun 2015-02-09 08:11:15

+1

數據列表被預先佔用,所以實際上我們可以將'acc'視爲一個堆棧。當我們完成循環(即命中停止條件)時,我們將我們的堆棧反轉,以便我們的函數以正常順序返回數據 – ivg 2015-02-09 11:15:34

1

這讀取文件的線,並打印它們:

open Core.Std 

let handle_line line = 
    printf "Your line: %s \n" line 

let() = 
    let file_to_read = "./file_to_read.txt" in 
    let lines = In_channel.read_lines file_to_read in 
     List.iter ~f: handle_line lines 
0

這裏是不累積的線條或使用外部庫,一個簡單的遞歸的解決方案,但可讓您閱讀使用有一條線,工藝函數,遞歸地讀取下一個,直到完成,然後乾淨地退出。 exit函數關閉打開的文件句柄,並向調用程序發出成功信號。

let read_lines file process = 
    let in_ch = open_in file in 
    let rec read_line() = 
    let line = try input_line in_ch with End_of_file -> exit 0 
    in (* process line in this block, then read the next line *) 
     process line; 
     read_line(); 
in read_line();; 

read_lines some_file print_endline;;