如何在F#中将文件读入行序列

69

这是 C# 版本:

public static IEnumerable<string> ReadLinesEnumerable(string path) {
  using ( var reader = new StreamReader(path) ) {
    var line = reader.ReadLine();
    while ( line != null ) {
      yield return line;
      line = reader.ReadLine();
    }
  }
}
但是直接翻译需要一个可变的变量。
6个回答

104
如果你正在使用.NET 4.0,你只需使用File.ReadLines即可。
> let readLines filePath = System.IO.File.ReadLines(filePath);;

val readLines : string -> seq<string>

1
这是否需要一次性将整个文件保存在内存中,还是可以逐行处理? - Nick Heiner
20
ReadLines和ReadAllLines方法的区别在于:当使用ReadLines时,您可以在整个字符串集合返回之前开始枚举字符串集合;而当使用ReadAllLines时,必须等待整个字符串数组返回后才能访问该数组。因此,在处理非常大的文件时,使用ReadLines可能更有效率。 - Joel Mueller

77
open System.IO

let readLines (filePath:string) = seq {
    use sr = new StreamReader (filePath)
    while not sr.EndOfStream do
        yield sr.ReadLine ()
}

谢谢!顺便问一下,有没有相关的库函数? - Yin Zhu
@David - 当然应该有。我相信.NET库正在逐渐向更多的IEnumerable接口转移。 - ChaosPandion
1
我需要读取另一个进程已经打开的文件,所以我进行了修改:use fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); use sr = new StreamReader(fs) - User
对于大多数人来说可能很明显,但还需要一个 open System.IO 指令。 - Mikeb

21

回答这个问题,是否有一个库函数可以封装这个模式 - 没有一个 完全 适用于此的函数,但是有一个函数可以让你从某个状态生成序列,叫做 Seq.unfold。你可以使用它来实现上面的功能,就像这样:

new StreamReader(filePath) |> Seq.unfold (fun sr -> 
  match sr.ReadLine() with
  | null -> sr.Dispose(); None 
  | str -> Some(str, sr))
sr 值表示流读取器,并作为状态传递。只要它给您非空值,您就可以返回一个包含要生成的元素和状态(如果需要,状态可以更改)的 Some 。当它读取到 null 时,我们将其处理掉并返回 None 来结束序列。这不是直接等效的,因为它在抛出异常时没有正确处理 StreamReader 的释放。
在这种情况下,我肯定会使用序列表达式(在大多数情况下更优雅且更易读),但了解使用高阶函数也很有用。

使用此代码时,我遇到了以下异常情况:{"无法从已关闭的TextReader中读取。"} 在match sr.ReadLine() with这行代码。请问有什么帮助或建议吗? - AruniRC
@AruniRC 我认为 @ChaosPandion 的解决方案比使用 unfold 的更好,所以我会选择它 :-) - Tomas Petricek
@AruniRC,Seq是惰性的——当您在代码中稍后评估它时,读取器可能已经关闭,因此会出现“无法从关闭的TextReader读取”的错误。您需要立即强制对序列进行评估,例如通过使用Seq.toList转换为列表或其他一些技巧。 - Mr. Curious

15
    let lines = File.ReadLines(path)                

    // To check
    lines |> Seq.iter(fun x -> printfn  "%s" x) 

5
在 .NET 2/3 中,您可以执行以下操作:
let readLines filePath = File.ReadAllLines(filePath) |> Seq.cast<string>

并且在.NET 4中:

let readLines filePath = File.ReadLines(filePath);;

1
其中第一个不是懒惰的(ReadAllLines 会急切地将所有行读入数组中)。 - Botond Balázs

0
为避免“System.ObjectDisposedException: Cannot read from a closed TextReader.”异常,请使用以下代码:
let lines = seq { yield! System.IO.File.ReadLines "/path/to/file.txt" }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接