2010-03-02 3 views
44

Ceci est la version C#:Comment lire un fichier dans un seq de lignes en 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(); 
    } 
    } 
} 

Mais une traduction directe des besoins variables mutables.

Répondre

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

Merci! BTW, existe-t-il une fonction de bibliothèque pour cela? –

+0

@David - Il devrait certainement être. Je crois que les bibliothèques .NET évoluent lentement vers plus d'interfaces IEnumerable. – ChaosPandion

+1

J'avais besoin de lire un fichier déjà ouvert par un autre processus donc j'ai modifié comme: 'use fs = new FileStream (filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); utilisez sr = new StreamReader (fs) ' – User

71

Si vous utilisez .NET 4.0, vous pouvez simplement utiliser File.ReadLines.

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

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

Est-ce que cela nécessite de conserver le fichier entier en mémoire en une seule fois, ou peut-il traiter ligne par ligne? –

+12

"Les méthodes ReadLines et ReadAllLines diffèrent comme suit: Lorsque vous utilisez ReadLines, vous pouvez commencer à énumérer la collection de chaînes avant que la collection entière soit renvoyée. Lorsque vous utilisez ReadAllLines, vous devez attendre que l'ensemble du tableau de chaînes soit renvoyé. Par conséquent, lorsque vous travaillez avec des fichiers très volumineux, ReadLines peut être plus efficace. " –

17

Pour répondre à la question de savoir s'il y a une fonction de bibliothèque pour encapsuler ce modèle - il n'y a pas une fonction exactement pour cela, mais il y a une fonction qui vous permet de générer des séquences à partir un état appelé Seq.unfold . Vous pouvez l'utiliser pour implémenter les fonctionnalités ci-dessus comme ceci:

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

La valeur sr représente le lecteur de flux et est passé comme l'état. Tant qu'il vous donne des valeurs non nulles, vous pouvez retourner Some contenant un élément à générer et l'état (qui pourrait changer si vous le vouliez). Quand il lit null, nous l'éliminons et retournons None pour terminer la séquence. Ce n'est pas un équivalent direct, car il ne dispose pas correctement StreamReader lorsqu'une exception est levée.

Dans ce cas, j'utiliserais certainement l'expression de séquence (qui est plus élégante et plus lisible dans la plupart des cas), mais il est utile de savoir qu'elle pourrait aussi être écrite en utilisant une fonction d'ordre supérieur.

+0

à l'aide de ceci j'obtiens l'exception suivante: {"Impossible de lire à partir d'un TextReader fermé."} À la ligne 'match sr.ReadLine() with'. toute aide s'il vous plaît quant à pourquoi? – AruniRC

+0

@AruniRC Je pense que la solution par @ChaosPandion est beaucoup plus agréable que celle utilisant 'unfold', donc je voudrais aller avec cela :-) –

+0

@AruniRC, le Seq est paresseux - au moment où vous l'évaluez plus tard dans le code , le lecteur pourrait déjà être fermé, d'où le 'Impossible de lire à partir d'un TextReader fermé». Vous devrez forcer l'évaluation de la séquence immédiatement, par exemple en convertissant en liste avec 'Seq.toList', ou un autre truc. –

3

Sur .NET 2/3 vous pouvez faire:

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

et .NET 4:

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

Le premier d'entre eux n'est pas paresseux (ReadAllLines lit avec empressement toutes les lignes dans un tableau). –

8
let lines = File.ReadLines(path)     

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