c# como - ¿Cómo leer el archivo de texto por un personaje particular separador de línea?




archivos vb (10)

Simplemente podría usar ReadToEnd () en el lector y luego usar String.Split para delimitarlo de cualquier forma que le parezca.

Lectura de un archivo de texto usando el lector de flujo.

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

Quiero forzar que el delimitador de línea sea \n not \r . Entonces, ¿cómo puedo hacer eso?


De acuerdo con la documentación:

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

Una línea se define como una secuencia de caracteres seguida de un avance de línea ("\ n"), un retorno de carro ("\ r"), o un retorno de carro seguido inmediatamente de un avance de línea ("\ r \ n").

De manera predeterminada, el método StreamReader ReadLine reconocerá una línea por ambos / cualquiera \ n o \ r


O bien tiene que analizar la secuencia byte por byte usted mismo y manejar la división, o necesita usar el comportamiento ReadLine predeterminado que se divide en / r, / n, / / ​​r / n.

Si quieres analizar la transmisión byte a byte, usaría algo así como el siguiente método de extensión:

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

Aunque dijiste "Usar StreamReader", ya que también dijiste "Mi caso, el archivo puede tener toneladas de registros ...", recomendaría probar SSIS. Es perfecto para lo que estás tratando de hacer. Puede procesar archivos muy grandes y especificar fácilmente los delimitadores de líneas / columnas.


Este fragmento de código leerá una línea de un archivo hasta que encuentre "\ 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();
          }
     }
}

Como este código lee carácter por carácter, funcionará con un archivo de cualquier longitud sin estar limitado por la memoria disponible.


Necesitaba una solución que fuera hasta "\ r \ n" y no se detuviera en "\ n". La solución de jp1980 funcionó, pero fue extremadamente lenta en un archivo grande. Entonces, convertí la solución de Mike Sackton para leer hasta encontrar una cadena especificada.

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

Y se llama así ...

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

Implementaría algo así como la respuesta de George, pero como método de extensión que evita cargar todo el archivo a la vez (no probado, pero algo como esto):

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

Que luego podría usarse como:

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

Me encantó la respuesta que dio @Pete. Me gustaría presentar una pequeña modificación. Esto le permitirá pasar un delimitador de cadena en lugar de un solo carácter:

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

Esta es una mejora de la respuesta Sovemp. Lo siento, me hubiera gustado comentar, aunque mi reputación no me permite hacerlo. Esta mejora aborda dos problemas:

  1. la secuencia de ejemplo "text \ rtest \ r \ n" con el delimitador "\ r \ n" también eliminaría la primera "\ r" que no está prevista.
  2. cuando los últimos caracteres en el flujo son iguales al delimitador, la función devolvería erróneamente una cadena incluyendo delimitadores.

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

¿Cómo convierto una cadena a un byte [] en .NET (C #) sin especificar manualmente una codificación específica?

Una string en .NET representa el texto como una secuencia de unidades de código UTF-16, por lo que los bytes ya están codificados en la memoria en UTF-16.

La respuesta de Mehrdad

Puedes usar la respuesta de Mehrdad , pero en realidad usa una codificación porque los caracteres son UTF-16. Llama a ToCharArray, que al ver la fuente crea char[]y copia la memoria directamente. Luego copia los datos a una matriz de bytes que también se asigna. Así que debajo del capó está copiando los bytes subyacentes dos veces y asignando una matriz de caracteres que no se usa después de la llamada.

La respuesta de Tom Blodget

La respuesta de Tom Blodget es un 20-30% más rápida que la de Mehrdad, ya que omite el paso intermedio de asignar una matriz de caracteres y copiar los bytes en ella, pero requiere que compiles con la /unsafeopción. Si no desea utilizar la codificación, creo que este es el camino a seguir. Si coloca su inicio de sesión de cifrado dentro del fixedbloque, ni siquiera necesita asignar una matriz de bytes separada y copiar los bytes en ella.

Además, ¿por qué debería considerarse la codificación? ¿No puedo simplemente obtener en qué bytes se ha almacenado la cadena? ¿Por qué hay una dependencia en las codificaciones de caracteres?

Porque esa es la forma correcta de hacerlo. stringEs una abstracción.

El uso de una codificación podría causarle problemas si tiene 'cadenas' con caracteres no válidos, pero eso no debería suceder. Si está obteniendo datos en su cadena con caracteres no válidos, lo está haciendo mal. Probablemente debería estar usando una matriz de bytes o una codificación Base64 para comenzar.

Si lo usas System.Text.Encoding.Unicode, tu código será más resistente. No tiene que preocuparse por la endianness del sistema en el que se ejecutará su código. No debe preocuparse si la próxima versión de CLR usará una codificación de caracteres interna diferente.

Creo que la pregunta no es por qué quieres preocuparte por la codificación, sino por qué quieres ignorarla y usar otra cosa. La codificación está destinada a representar la abstracción de una cadena en una secuencia de bytes. System.Text.Encoding.Unicodele dará una pequeña codificación de bytes de bytes endian y funcionará de la misma manera en todos los sistemas, ahora y en el futuro.





c# .net file-handling