How read a file into a seq of lines in F#

f#
39,191

Solution 1

open System.IO

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

Solution 2

If you're using .NET 4.0, you can just use File.ReadLines.

> let readLines filePath = System.IO.File.ReadLines(filePath);;

val readLines : string -> seq<string>

Solution 3

To answer the question whether there is a library function for encapsulating this pattern - there isn't a function exactly for this, but there is a function that allows you to generate sequence from some state called Seq.unfold. You can use it to implement the functionality above like this:

new StreamReader(filePath) |> Seq.unfold (fun sr -> 
  match sr.ReadLine() with
  | null -> sr.Dispose(); None 
  | str -> Some(str, sr))

The sr value represents the stream reader and is passed as the state. As long as it gives you non-null values, you can return Some containing an element to generate and the state (which could change if you wanted). When it reads null, we dispose it and return None to end the sequence. This isn't a direct equivalent, because it doesn't properly dispose StreamReader when an exception is thrown.

In this case, I would definitely use sequence expression (which is more elegant and more readable in most of the cases), but it's useful to know that it could be also written using a higher-order function.

Solution 4

    let lines = File.ReadLines(path)                

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

Solution 5

On .NET 2/3 you can do:

let readLines filePath = File.ReadAllLines(filePath) |> Seq.cast<string>

and on .NET 4:

let readLines filePath = File.ReadLines(filePath);;
Share:
39,191
Yin Zhu
Author by

Yin Zhu

quant

Updated on December 08, 2020

Comments

  • Yin Zhu
    Yin Zhu over 3 years

    This is C# version:

    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();
        }
      }
    }
    

    But directly translating needs a mutable variable.

  • Yin Zhu
    Yin Zhu about 14 years
    Thanks! Btw, is there a library function for that?
  • ChaosPandion
    ChaosPandion about 14 years
    @David - There certainly should be. I believe the .NET libraries are slowly moving towards more IEnumerable interfaces.
  • AruniRC
    AruniRC almost 12 years
    on using this i am getting the following exception: {"Cannot read from a closed TextReader."} at the match sr.ReadLine() with line. any help please as to why?
  • Nick Heiner
    Nick Heiner almost 12 years
    Does that require holding the entire file in memory at once, or can it process line by line?
  • Joel Mueller
    Joel Mueller almost 12 years
    "The ReadLines and ReadAllLines methods differ as follows: When you use ReadLines, you can start enumerating the collection of strings before the whole collection is returned; when you use ReadAllLines, you must wait for the whole array of strings be returned before you can access the array. Therefore, when you are working with very large files, ReadLines can be more efficient."
  • Tomas Petricek
    Tomas Petricek over 11 years
    @AruniRC I think the solution by @ChaosPandion is much nicer than the one using unfold, so I would go with that :-)
  • User
    User almost 11 years
    I needed to read a file already opened by another process so I modified as: use fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); use sr = new StreamReader(fs)
  • Mr. Curious
    Mr. Curious about 9 years
    @AruniRC, the Seq is lazy--by the time you evaluate it later in the code, the reader might already be closed, hence the Cannot read from a closed TextReader. You will have to force evaluation of the sequence immediately, for example by converting to list with Seq.toList, or some other trick.
  • Botond Balázs
    Botond Balázs over 7 years
    The first one of these is not lazy (ReadAllLines eagerly reads all lines into an array).
  • Mikeb
    Mikeb over 4 years
    probably obvious to most but also needs an open System.IO directive