web-dev-qa-db-fra.com

Comment lire un fichier texte avec un séparateur de lignes particulier?

Lecture d'un fichier texte à l'aide de streamreader.

using (StreamReader sr = new StreamReader(FileName, Encoding.Default))
{
     string line = sr.ReadLine();
}

Je veux forcer ce délimiteur de ligne devrait être \n et non pas \r. Alors, comment puis-je faire ça?

32
User13839404
string text = sr.ReadToEnd();
string[] lines = text.Split('\r');
foreach(string s in lines)
{
   // Consume
}
22
George Johnston

J'implémenterais quelque chose comme la réponse de George, mais comme méthode d'extension évitant de charger tout le fichier à la fois (non testé, mais quelque chose comme ceci):

static class ExtensionsForTextReader
{
     public static IEnumerable<string> ReadLines (this TextReader reader, char delimiter)
     {
            List<char> chars = new List<char> ();
            while (reader.Peek() >= 0)
            {
                char c = (char)reader.Read ();

                if (c == delimiter) {
                    yield return new String(chars.ToArray());
                    chars.Clear ();
                    continue;
                }

                chars.Add(c);
            }
     }
}

Ce qui pourrait alors être utilisé comme:

using (StreamReader sr = new StreamReader(FileName, Encoding.Default))
{
     foreach (var line in sr.ReadLines ('\n'))
           Console.WriteLine (line);
}
33
Pete

J'ai adoré la réponse donnée par @Pete. Je voudrais juste soumettre une légère modification. Cela vous permettra de passer un séparateur de chaîne au lieu d'un seul caractère:

using System;
using System.IO;
using System.Collections.Generic;
internal static class StreamReaderExtensions
{
    public static IEnumerable<string> ReadUntil(this StreamReader reader, string delimiter)
    {
        List<char> buffer = new List<char>();
        CircularBuffer<char> delim_buffer = new CircularBuffer<char>(delimiter.Length);
        while (reader.Peek() >= 0)
        {
            char c = (char)reader.Read();
            delim_buffer.Enqueue(c);
            if (delim_buffer.ToString() == delimiter || reader.EndOfStream)
            {
                if (buffer.Count > 0)
                {
                    if (!reader.EndOfStream)
                    {
                        yield return new String(buffer.ToArray()).Replace(delimiter.Substring(0, delimiter.Length - 1), string.Empty);
                    }
                    else
                    {
                        buffer.Add(c);
                        yield return new String(buffer.ToArray());
                    }
                    buffer.Clear();
                }
                continue;
            }
            buffer.Add(c);
        }
    }

    private class CircularBuffer<T> : Queue<T>
    {
        private int _capacity;

        public CircularBuffer(int capacity)
            : base(capacity)
        {
            _capacity = capacity;
        }

        new public void Enqueue(T item)
        {
            if (base.Count == _capacity)
            {
                base.Dequeue();
            }
            base.Enqueue(item);
        }

        public override string ToString()
        {
            List<String> items = new List<string>();
            foreach (var x in this)
            {
                items.Add(x.ToString());
            };
            return String.Join("", items);
        }
    }
}
7
sovemp

Selon la documentation:

http://msdn.Microsoft.com/en-us/library/system.io.streamreader.readline.aspx

Une ligne est définie comme une séquence de caractères suivie d'un saut de ligne ("\ n"), un retour de chariot ("\ r") ou un retour de chariot immédiatement suivi d'un saut de ligne ("\ r\n").

Par défaut, la méthode StreamReader ReadLine reconnaîtra une ligne à la fois/\ n ou\r

6
Martin

C'est une amélioration de la réponse de sovemp. Désolé, j'aurais aimé commenter, bien que ma réputation ne me permette pas de le faire. Cette amélioration concerne 2 problèmes:

  1. la séquence exemple "text\rtest\r\n" avec le délimiteur "\ r\n" effacerait également le premier "\ r" qui n’était pas destiné.
  2. lorsque les derniers caractères du flux sont égaux au délimiteur, la fonction renverrait à tort une chaîne, y compris des délimiteurs.

    using System;
    using System.IO;
    using System.Collections.Generic;
    internal static class StreamReaderExtensions
    {
        public static IEnumerable<string> ReadUntil(this StreamReader reader, string delimiter)
        {
            List<char> buffer = new List<char>();
            CircularBuffer<char> delim_buffer = new CircularBuffer<char>(delimiter.Length);
            while (reader.Peek() >= 0)
            {
                char c = (char)reader.Read();
                delim_buffer.Enqueue(c);
                if (delim_buffer.ToString() == delimiter || reader.EndOfStream)
                {
                    if (buffer.Count > 0)
                    {
                        if (!reader.EndOfStream)
                        {
                            buffer.Add(c);
                            yield return new String(buffer.ToArray()).Substring(0, buffer.Count - delimeter.Length);
                        }
                        else
                        {
                            buffer.Add(c);
                            if (delim_buffer.ToString() != delimiter)
                                yield return new String(buffer.ToArray());
                            else
                                yield return new String(buffer.ToArray()).Substring(0, buffer.Count - delimeter.Length);
                        }
                        buffer.Clear();
                    }
                    continue;
                }
                buffer.Add(c);
            }
        }
    
        private class CircularBuffer<T> : Queue<T>
        {
            private int _capacity;
    
            public CircularBuffer(int capacity)
                : base(capacity)
            {
                _capacity = capacity;
            }
    
            new public void Enqueue(T item)
            {
                if (base.Count == _capacity)
                {
                    base.Dequeue();
                }
                base.Enqueue(item);
            }
    
            public override string ToString()
            {
                List<String> items = new List<string>();
                foreach (var x in this)
                {
                    items.Add(x.ToString());
                };
                return String.Join("", items);
            }
        }
    }
    
3
jp1980

Vous devez soit analyser le flux, octet par octet, vous-même et gérer le fractionnement, soit utiliser le comportement par défaut de ReadLine, qui scinde sur/r,/n ou/r/n.

Si vous voulez analyser le flux octet par octet, j'utiliserais quelque chose comme la méthode d'extension suivante:

 public static string ReadToChar(this StreamReader sr, char splitCharacter)
    {        
        char nextChar;
        StringBuilder line = new StringBuilder();
        while (sr.Peek() > 0)
        {               
            nextChar = (char)sr.Read();
            if (nextChar == splitCharacter) return line.ToString();
            line.Append(nextChar);
        }

        return line.Length == 0 ? null : line.ToString();
    }
3
Mike Sackton

J'avais besoin d'une solution qui se lit jusqu'à "\ r\n", et ne s'arrête pas à "\ n". La solution de jp1980 a fonctionné, mais était extrêmement lente sur un fichier volumineux. J'ai donc converti la solution de Mike Sackton en lecture jusqu'à ce qu'une chaîne spécifiée soit trouvée.

public static string ReadToString(StreamReader sr, string splitString)
{        
    char nextChar;
    StringBuilder line = new StringBuilder();
    int matchIndex = 0;

    while (sr.Peek() > 0)
    {               
        nextChar = (char)sr.Read();
        line.Append(nextChar);
        if (nextChar == splitString[matchIndex])
        {
            if(matchIndex == splitString.Length - 1)
            {
                return line.ToString().Substring(0, line.Length - splitString.Length);
            }
            matchIndex++;
        }
        else
        {
            matchIndex = 0;
        }
    }

    return line.Length == 0 ? null : line.ToString();
}

Et ça s'appelle comme ça ...

using (StreamReader reader = new StreamReader(file))
{
    string line;
    while((line = ReadToString(reader, "\r\n")) != null)
    {
        Console.WriteLine(line);
    }
}
2
William S.

Même si vous avez dit "Using StreamReader", puisque vous avez également déclaré "Mon cas, le fichier peut contenir des tonnes d'enregistrements ...", je recommanderais d'essayer SSIS. C'est parfait pour ce que vous essayez de faire. Vous pouvez traiter un fichier très volumineux et spécifier facilement les délimiteurs de ligne/colonne.

1
Tipx

Vous pouvez simplement utiliser ReadToEnd () sur le lecteur, puis utiliser String.Split pour le délimiter comme bon vous semble.

0
Brandon Moretz

Cet extrait de code lira une ligne d'un fichier jusqu'à ce qu'il rencontre "\ n". 

using (StreamReader sr = new StreamReader(path)) 
{
     string line = string.Empty;
     while (sr.Peek() >= 0) 
     {
          char c = (char)sr.Read();
          if (c == '\n')
          {
              //end of line encountered
              Console.WriteLine(line);
              //create new line
              line = string.Empty;
          }
          else
          {
               line += (char)sr.Read();
          }
     }
}

Parce que ce code lit caractère par caractère, il fonctionne avec un fichier de n'importe quelle longueur sans être contraint par la mémoire disponible.

0
Dan Waterbly