2010-03-02 6 views
44

Diese C# Version ist:Wie lesen Sie eine Datei in eine seq von Linien in F #

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

Aber direkt Bedürfnisse eine veränderbare Variable zu übersetzen.

Antwort

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

Dank! Übrigens, gibt es dafür eine Bibliotheksfunktion? –

+0

@David - Es sollte sicherlich sein. Ich glaube, die .NET-Bibliotheken bewegen sich langsam hin zu mehr IEnumerable-Schnittstellen. – ChaosPandion

+1

Ich musste eine Datei lesen, die bereits von einem anderen Prozess geöffnet wurde, also änderte ich: 'uses fs = new FileStream (Dateipfad, Dateimodus.Öffnen, Dateizugriff.Read, DateiShare.ReadWrite); Verwenden Sie sr = new StreamReader (fs) ' – User

71

Wenn Sie 4.0 verwenden sind .NET, können Sie einfach File.ReadLines verwenden.

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

val readLines : string -> seq<string> 
+0

Muss dafür die gesamte Datei im Speicher gehalten werden, oder kann sie Zeile für Zeile verarbeitet werden? –

+12

"Die ReadLines- und die ReadAllLines-Methode unterscheiden sich wie folgt: Wenn Sie ReadLines verwenden, können Sie die Auflistung der Zeichenfolgen auflisten, bevor die gesamte Auflistung zurückgegeben wird. Wenn Sie ReadAllLines verwenden, müssen Sie warten, bis das gesamte Array zurückgegeben wird Wenn Sie mit sehr großen Dateien arbeiten, können ReadLines effizienter sein. " –

17

Zur Beantwortung der Frage, ob es eine Bibliotheksfunktion zum Einkapseln dieses Musters ist - gibt es keine Funktion genau für diese, aber es gibt eine Funktion, die Sie Sequenz generieren kann von einigem Zustand Seq.unfold genannt . Sie können es verwenden, um die Funktionalität oben wie folgt umzusetzen:

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

Der sr Wert stellt den Stromleser und wird als der Staat übergeben. Solange es Ihnen Nicht-Null-Werte gibt, können Sie Some mit einem zu generierenden Element und dem Status (der sich ändern könnte, wenn Sie möchten) zurückgeben. Wenn es null liest, entsorgen wir es und geben None zurück, um die Sequenz zu beenden. Dies ist kein direktes Äquivalent, da StreamReader beim Auslösen einer Ausnahme nicht ordnungsgemäß entsorgt wird.

In diesem Fall würde ich definitiv Sequenzausdruck verwenden (der in den meisten Fällen eleganter und lesbarer ist), aber es ist nützlich zu wissen, dass er auch mit einer Funktion höherer Ordnung geschrieben werden könnte.

+0

Bei der Verwendung dieser Funktion erhalte ich die folgende Ausnahme: {"Kann von einem geschlossenen TextReader nicht lesen."} In der Zeile 'match sr.ReadLine() with'. irgendwelche Hilfe bitte warum? – AruniRC

+0

@AruniRC Ich denke, die Lösung von @ChaosPandion ist viel schöner als die mit 'Entfalten', also würde ich mit dem gehen :-) –

+0

@AruniRC, die Seq ist faul - bis Sie es später im Code auswerten , der Leser könnte bereits geschlossen sein, daher das 'Kann nicht von einem geschlossenen TextReader lesen '. Sie müssen die Auswertung der Sequenz sofort erzwingen, zum Beispiel indem Sie in die Liste mit 'Seq.toList' oder einem anderen Trick umwandeln. –

3

auf 2/3 .NET können Sie tun:

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

und auf .NET 4:

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

Der erste von diesen ist nicht faul ('ReadAlllines' liest eifrig alle Zeilen in ein Array). –

8
let lines = File.ReadLines(path)     

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