58 votes

C # Lecture d'un fichier ligne par ligne

Je suis en train de lire des fichiers texte où chaque ligne doit être traité. Pour le moment je suis juste en utilisant un StreamReader, et puis à la lecture de chaque ligne individuellement.

Je me demande si il existe un moyen plus efficace (en termes de LoC et la lisibilité) pour ce faire à l'aide de LINQ sans compromettre l'efficacité opérationnelle. Les exemples que j'ai vu impliquer le chargement de tout le fichier en mémoire, puis de les traiter. Cependant dans ce cas, je ne crois pas que ce serait très efficace. Dans le premier exemple, les fichiers peuvent obtenir jusqu'à environ 50k, et dans le deuxième exemple, toutes les lignes du fichier doit être lu (les tailles sont généralement < 10 km).

On pourrait dire que, de nos jours, il n'a pas vraiment d'importance pour ces petits fichiers, mais je crois que sorte de l'approche conduit à l'inefficacité de code.

Merci pour votre temps!

Premier exemple:

// open file
using(var file = System.IO.File.OpenText(_LstFilename))
{
    // read file
    while (!file.EndOfStream)
    {
        String line = file.ReadLine();

        // ignore empty lines
        if (line.Length > 0)
        {
            // create addon
            T addon = new T();
            addon.Load(line, _BaseDir);

            // add to collection
            collection.Add(addon);
        }
    }
}

Deuxième exemple:

// open file
using (var file = System.IO.File.OpenText(datFile))
{
    // compile regexs
    Regex nameRegex = new Regex("IDENTIFY (.*)");

    while (!file.EndOfStream)
    {
        String line = file.ReadLine();

        // check name
        Match m = nameRegex.Match(line);
        if (m.Success)
        {
            _Name = m.Groups[1].Value;

            // remove me when other values are read
            break;
        }
    }
}

95voto

Marc Gravell Points 482669

Vous pouvez écrire un LINQ ligne basée sur le lecteur assez facilement à l'aide d'un itérateur bloc:

static IEnumerable<SomeType> ReadFrom(string file) {
    string line;
    using(var reader = File.OpenText(file)) {
        while((line = reader.ReadLine()) != null) {
            SomeType newRecord = /* parse line */
            yield return newRecord;
        }
    }
}

ou pour faire de Jon heureux:

static IEnumerable<string> ReadFrom(string file) {
    string line;
    using(var reader = File.OpenText(file)) {
        while((line = reader.ReadLine()) != null) {
            yield return line;
        }
    }
}
...
var typedSequence = from line in ReadFrom(path)
                    let record = ParseLine(line)
                    where record.Active // for example
                    select record.Key;

ensuite, vous avez ReadFrom(...) comme un paresseusement évalué séquence sans mise en mémoire tampon, parfait pour l' Where etc.

Notez que si vous utilisez OrderBy ou de la norme GroupBy, il va avoir de la mémoire tampon les données dans la mémoire; si vous besoin de regroupement et d'agrégation, "PushLINQ" a peu de fantaisie de code pour vous permettre d'effectuer des agrégations sur les données, mais de les jeter (pas de tampon). Jon explication est ici.

23voto

Jon Skeet Points 692016

Il est plus simple de lire une ligne et de vérifier si oui ou non nul que pour vérifier EndOfStream tout le temps.

Cependant, j'ai aussi un LineReader de la classe dans MiscUtil qui rend tout cela beaucoup plus simple - fondamentalement, il expose un fichier (ou un Func<TextReader> comme IEnumerable<string> qui permet de faire LINQ des trucs sur elle. Ainsi, vous pouvez faire des choses comme:

var query = from file in Directory.GetFiles("*.log")
            from line in new LineReader(file)
            where line.Length > 0
            select new AddOn(line); // or whatever

Le cœur de l' LineReader est-ce la mise en œuvre de l' IEnumerable<string>.GetEnumerator:

public IEnumerator<string> GetEnumerator()
{
    using (TextReader reader = dataSource())
    {
        string line;
        while ((line = reader.ReadLine()) != null)
        {
            yield return line;
        }
    }
}

Presque tout le reste de la source est de simplement donner de la souplesse de la configuration d' dataSource (ce qui est une Func<TextReader>).

1voto

David Kemp Points 5711

REMARQUE : vous devez surveiller la solution IEnumerable<T> , car le fichier sera ouvert pendant la durée du traitement.

Par exemple, avec la réponse de Marc Gravell:

 foreach(var record in ReadFrom("myfile.csv")) {
    DoLongProcessOn(record);
}
 

le fichier restera ouvert pendant tout le traitement.

0voto

Luca Spiller Points 955

Merci à tous pour vos réponses! J'ai décidé d'aller avec un mélange, se concentrant principalement sur de Marc, mais, comme j'ai seulement besoin de lire les lignes d'un fichier. Je suppose qu'on pourrait dire de séparation est nécessaire partout, mais hé, la vie est trop courte!

Concernant la tenue du fichier ouvert, qui ne va pas être un problème dans ce cas, le code est une partie d'une application de bureau.

Enfin, j'ai remarqué que vous tous utilisés minuscule string. Je sais en Java il y a une différence entre capitalisés et non capitalisés de la chaîne, mais j'ai pensé en C# minuscule string était juste une référence à capitalisés Chaîne?

public void Load(AddonCollection<T> collection)
{
    // read from file
    var query =
        from line in LineReader(_LstFilename)
        where line.Length > 0
        select CreateAddon(line);

    // add results to collection
    collection.AddRange(query);
}

protected T CreateAddon(String line)
{
    // create addon
    T addon = new T();
    addon.Load(line, _BaseDir);

    return addon;
}

protected static IEnumerable<String> LineReader(String fileName)
{
    String line;
    using (var file = System.IO.File.OpenText(fileName))
    {
        // read each line, ensuring not null (EOF)
        while ((line = file.ReadLine()) != null)
        {
            // return trimmed line
            yield return line.Trim();
        }
    }
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X