2011-04-25 127 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;;