2010-03-02 11 views
44

To jest wersja C#:Jak odczytać plik do nast linii w 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(); 
    } 
    } 
} 

lecz bezpośrednio tłumaczenia potrzebuje zmienne zmienny.

Odpowiedz

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

Dzięki! Btw, czy istnieje do tego funkcja biblioteczna? –

+0

@ David - Z pewnością powinno być. Wierzę, że biblioteki .NET powoli zmierzają w kierunku większej liczby interfejsów IEnumerable. – ChaosPandion

+1

Musiałem przeczytać plik już otwarty przez inny proces, więc zmodyfikowałem go jako: 'use fs = new FileStream (filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); użyj sr = new StreamReader (fs) ' – User

71

Jeśli używasz .NET 4.0, możesz po prostu użyć File.ReadLines.

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

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

Czy to wymaga zatrzymania całego pliku w pamięci na raz, czy może przetwarzać linię po linii? –

+12

"Metody ReadLines i ReadAllLines różnią się w następujący sposób: Kiedy używasz ReadLines, możesz rozpocząć wyliczanie kolekcji ciągów przed zwróceniem całej kolekcji, podczas korzystania z ReadAllLines, musisz poczekać aż cała tablica łańcuchów zostanie zwrócony, zanim będziesz mógł dostęp do tablicy, dlatego gdy pracujesz z bardzo dużymi plikami, ReadLines mogą być bardziej wydajne. " –

17

Aby odpowiedzieć na pytanie, czy istnieje funkcja biblioteki dla enkapsulacji ten wzór - nie jest funkcją dokładnie za to, ale nie jest to funkcja, która pozwala na generowanie sekwencji z jakiś stan zwany Seq.unfold . Można go używać do wdrożenia funkcjonalność powyżej tak:

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

Wartość sr przedstawia czytelnikowi strumienia i jest przekazywana jako państwa. Dopóki daje wartości inne niż NULL, możesz zwrócić Some zawierający element do wygenerowania i stan (który może się zmienić, jeśli chcesz). Kiedy czyta się null, pozbawiamy go i zwracamy None, aby zakończyć sekwencję. Nie jest to bezpośredni odpowiednik, ponieważ nie wywołuje on prawidłowo StreamReader podczas zgłaszania wyjątku.

W tym przypadku zdecydowanie użyłbym wyrażenia sekwencji (które jest bardziej eleganckie i bardziej czytelne w większości przypadków), ale warto wiedzieć, że można go również napisać przy użyciu funkcji wyższego rzędu.

+0

przy użyciu tego otrzymuję następujący wyjątek: {"Nie można odczytać z zamkniętego TextReader."} W linii 'match sr.ReadLine() with'. jakiejkolwiek pomocy proszę, dlaczego? – AruniRC

+0

@AruniRC Myślę, że rozwiązanie @ChaosPandion jest o wiele lepsze niż przy użyciu 'unfold', więc chciałbym przejść do tego :-) –

+0

@AruniRC, Seq jest leniwy - do czasu, kiedy ocenisz go później w kodzie , Czytelnik może już być zamknięty, stąd "Nie można odczytać z zamkniętego TextReadera". Będziesz musiał wymusić natychmiastową ocenę sekwencji, na przykład poprzez konwersję na listę z 'Seq.toList' lub inną lewę. –

3

Na .NET 2/3 można zrobić:

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

i na .NET 4:

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

Pierwszy z nich nie jest leniwy ("ReadAllLines" chętnie czyta wszystkie linie w tablicy). –

8
let lines = File.ReadLines(path)     

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