File io 一元文件I/O

File io 一元文件I/O,file-io,ocaml,monads,File Io,Ocaml,Monads,有很多关于如何读取和写入文件的例子,但许多帖子似乎过时了,或者不“安全”(,)(它们抛出/引发异常)。来自Rust,我想用一元代码(如result)来显式处理所有错误 下面是一个“安全er”的尝试,因为打开和读/写操作不会引发/引发。但不确定收盘是否会失败。有没有更简洁、更安全的方法 (*opam安装核心电池*) 开放式Stdio 开放式电池 打开结果。中缀 让读取安全(文件路径:string):(string,exn)batpervisives.result= (尝试让chan=In_chan

有很多关于如何读取和写入文件的例子,但许多帖子似乎过时了,或者不“安全”(,)(它们抛出/引发异常)。来自Rust,我想用一元代码(如
result
)来显式处理所有错误

下面是一个“安全er”的尝试,因为打开和读/写操作不会引发/引发。但不确定收盘是否会失败。有没有更简洁、更安全的方法

(*opam安装核心电池*)
开放式Stdio
开放式电池
打开结果。中缀
让读取安全(文件路径:string):(string,exn)batpervisives.result=
(尝试让chan=In_channel.create file_path In Ok(chan)
使用(e:exn)->错误(e))
>>=陈芳->
设res_字符串=
尝试
设b=输入信道。输入信道
好的(b)
使用(e:exn)->中的错误(e)
In_channel.close chan;
BatResult.map(趣味字符串->字符串.concat“\n”字符串)res\u字符串
让write_safe(文件路径:string)(文本:string):(单位,exn)batpervisives.result=
(试试看
(让chan=Out_channel.create file_path in Ok(chan))
使用(e:exn)->错误(e))
>>=陈芳->
设res=
(试着让b=Out_channel.output_string chan文本进入Ok(b)
中有(e:exn)->错误(e))
出频道,关闭频道;
物件
让()=
放出=
读取\u safe“test in.txt”
>>=fun str->write_safe“test out.txt”str in
BatResult.iter_错误(fun e->打印_结束行(Base.Exn.to_string e))输出

作为Janestreet industrial strength标准库的一部分的
Stdio
库已经提供了这样的功能,这些功能当然是安全的,例如,将文件的内容读取为字符串,并相应地将其写入文件,因此我们可以实现
cp
实用程序

(* file cp.ml *)
(* file cp.ml *)
open Base
open Stdio

let () = match Sys.get_argv () with
  | [|_cp; src; dst |] ->
    Out_channel.write_all dst
      ~data:(In_channel.read_all src)
  | _ -> invalid_arg "Usage: cp src dst"
要构建和运行代码,请将其放入
cp.ml
文件(理想情况下是在新目录中),然后运行

此命令将使用引导您的项目。然后,您可以使用

dune exec ./cp.exe cp.ml cp.copy.ml
下面是到的链接,它将使您更容易在OCaml中找到感兴趣的库

此外,如果要将引发异常的函数转换为返回错误的函数,则可以使用,例如


以下是基于@ivg answer(非常好)的完整安全解决方案,只使用
Base

(请把他的答案投上去,不要投这个。)

openbase
开放式基地。结果
开放式Stdio
让读取安全(文件路径:字符串)=
Result.try_with@@fun()->
In_channel.read_所有文件路径
让写入安全(文件路径:字符串)(文本:字符串)=
Result.try_with@@fun()->
Out\u channel.write\u all~数据:文本文件路径
让()=
放出=
读取\u safe“test in.txt”
>>=fun str->
将_safe“test out.txt”str写入
iter\u错误输出~f:(fune e->print_endline(Base.Exn.to_string e))

您可以在OCaml中读写文件,而无需其他标准库。您所需的一切都已内置到OCaml中

下面是一个读取文件的示例,同时确保在发生异常时安全关闭文件描述符:。从那里,您可以编写类似的函数,使用相应的函数
open\u out
out\u channel\u length
output
编写文件

这些读写文件内容是OCaml的
字节
类型,即可变的bytestring。但是,它们可能抛出异常。这很好。在OCaml中,异常是廉价且易于处理的。尽管如此,有时人们出于任何原因都不喜欢它们。因此,现在对抛出异常的函数加后缀是一种惯例。因此,假设您将上述两个函数定义为:

val get_contents_exn : string -> bytes
val set_contents_exn : string -> bytes -> unit
现在,您(或任何人)都可以轻松地包装它们并返回一个
结果
值,如Rust。但是,由于我们在OCaml中有多态变体,我们利用它来组合函数,这些函数可以返回
结果
值,如下所述:

所以你可以这样包装它们:

let get_contents filename =
  try Ok (get_contents_exn filename) with exn -> Error (`Exn exn)

let set_contents filename contents =
  try Ok (set_contents_exn filename contents) with exn -> Error (`Exn exn)
现在有以下类型:

val get_contents : string -> (bytes, [> `Exn of exn]) result
val set_contents : string -> bytes -> (unit, [> `Exn of exn]) result
它们可以相互组合在一起,也可以与其他函数组合在一起,这些函数通过多态变量错误通道返回
结果


我想在这里指出的一点是,为您的用户提供这两种服务,这样他们就可以选择对他们有意义的方式——例外或
result
s。但是,堆栈溢出不是一个代码审查论坛。我强烈建议在这里结束这个问题,并在OCaml论坛上提问。那里的许多人都渴望提供帮助。最突出的问题是你混合了不同的标准库。我建议你坚持一个。您可以访问discussion.ocaml.org进行更深入的讨论。除了已经给出的其他评论和答案,因为您关心异常安全注意,如果您使用异常,ocaml有一个有趣的.protect函数,其行为非常类似于unwind protect(最后是关于该问题的条款).@yawar对不起,我知道。这个问题实际上并不是为了得到代码审查。我所要做的就是生成一些更有用、更现代的内容,这样其他人就可以从中受益。与其他语言(js、c#、rust等)相比,谷歌在基本功能上的每一次搜索都是垃圾。只是帮助下一个初学者(或两个月后的我)。事实上,我们错过了那些典型编程任务的简单片段、问题和答案,这些都是其他语言的。因此,我支持你的倡议。
let get_contents filename =
  try Ok (get_contents_exn filename) with exn -> Error (`Exn exn)

let set_contents filename contents =
  try Ok (set_contents_exn filename contents) with exn -> Error (`Exn exn)
val get_contents : string -> (bytes, [> `Exn of exn]) result
val set_contents : string -> bytes -> (unit, [> `Exn of exn]) result