¿La forma más fácil de dividir una cadena en nuevas líneas en .NET?

806

Necesito dividir una cadena en nuevas líneas en .NET y la única forma en que sé dividir cadenas es con el método Split . Sin embargo, eso no me permitirá dividir (fácilmente) en una nueva línea, entonces, ¿cuál es la mejor manera de hacerlo?

RCIX
fuente
2
¿Por qué no lo haría? Solo dividir en System.Environment.NewLine
aviraldg
16
Pero tiene que envolverlo en una cadena [] y agregar un argumento adicional y ... simplemente se siente torpe.
RCIX

Respuestas:

1414

Para dividir en una cadena, debe usar la sobrecarga que toma una matriz de cadenas:

string[] lines = theText.Split(
    new[] { Environment.NewLine },
    StringSplitOptions.None
);

Editar:
si desea manejar diferentes tipos de saltos de línea en un texto, puede usar la capacidad de hacer coincidir más de una cadena. Esto se dividirá correctamente en cualquier tipo de salto de línea y conservará las líneas vacías y el espaciado en el texto:

string[] lines = theText.Split(
    new[] { "\r\n", "\r", "\n" },
    StringSplitOptions.None
);
Guffa
fuente
3
@RCIX: Enviar los parámetros correctos al método es un poco incómodo porque lo está utilizando para algo que es mucho más simple de lo que es capaz. Al menos está ahí, antes de marco 2 se tenía que utilizar una expresión regular o construir su propia rutina de división para dividir en una cuerda ...
Guffa
44
@Leandro: la Environment.NewLinepropiedad contiene la nueva línea predeterminada para el sistema. Para un sistema Windows, por ejemplo, lo será "\r\n".
Guffa
3
@Leandro: Una suposición sería que el programa se divide al \ndejar un \ral final de cada línea, y luego genera las líneas con un \r\nentre ellos.
Guffa
3
@Samuel: Las secuencias de escape \ry \n(entre otras) tienen un significado especial para el compilador de C #. VB no tiene esas secuencias de escape, por lo que se utilizan esas constantes en su lugar.
Guffa
2
Si desea aceptar archivos de varios sistemas operativos, también puede agregar "\ n \ r" al inicio y "\ r" al final de la lista del delimitador. Sin embargo, no estoy seguro de que valga la pena el rendimiento. ( en.wikipedia.org/wiki/Newline )
user420667
121

¿Qué hay de usar un StringReader?

using (System.IO.StringReader reader = new System.IO.StringReader(input)) {
    string line = reader.ReadLine();
}
Clemente
fuente
13
Este es mi favorito. Envuelto en un método de extensión y rendimiento de la línea actual de retorno: gist.github.com/ronnieoverby/7916886
Ronnie Overby
3
Esta es la única solución no regex que he encontrado para .netcf 3.5
Carl
8
Especialmente agradable cuando la entrada es grande y copiarla en una matriz se vuelve lenta / intensiva en memoria.
Alejandro
1
Tal como está escrito, esta respuesta solo lee la primera línea. Vea la respuesta de Steve Cooper para el whileciclo que debe agregarse a esta respuesta.
ToolmakerSteve
48

Debería poder dividir su cadena con bastante facilidad, así:

aString.Split(Environment.NewLine.ToCharArray());
nikmd23
fuente
46
En un sistema que no sea * nix que se dividirá en los caracteres separados en la cadena de Newline, es decir, los caracteres CR y LF. Eso provocará una cadena vacía adicional entre cada línea.
Guffa
Corrígeme si estoy equivocado, pero ¿eso no se dividirá en los caracteres \ yn?
RCIX
77
@RCIX: No, los códigos \ r y \ n representan caracteres individuales. La cadena "\ r \ n" tiene dos caracteres, no cuatro.
Guffa
10
si agrega el parámetro StringSplitOptions.RemoveEmptyEntries, esto funcionará perfectamente.
Ruben
18
@ Rubén: No, no lo hará. Serge ya sugirió eso en su respuesta, y ya he explicado que también eliminará las líneas vacías en el texto original que deben conservarse.
Guffa el
34

Intente evitar usar string.Split para obtener una solución general, porque usará más memoria en todos los lugares donde use la función: la cadena original y la copia dividida, ambas en la memoria. Confía en mí que esto puede ser un gran problema cuando comienzas a escalar: ejecuta una aplicación de procesamiento por lotes de 32 bits que procesa documentos de 100 MB y obtendrás ocho hilos simultáneos. No es que haya estado allí antes ...

En su lugar, use un iterador como este;

    public static IEnumerable<string> SplitToLines(this string input)
    {
        if (input == null)
        {
            yield break;
        }

        using (System.IO.StringReader reader = new System.IO.StringReader(input))
        {
            string line;
            while( (line = reader.ReadLine()) != null)
            {
                yield return line;
            }
        }
    }

Esto le permitirá hacer un ciclo de memoria más eficiente alrededor de sus datos;

foreach(var line in document.SplitToLines()) 
{
    // one line at a time...
}

Por supuesto, si lo quieres todo en la memoria, puedes hacerlo;

var allTheLines = document.SplitToLines.ToArray();
Steve Cooper
fuente
He estado allí ... (analizando archivos HTML grandes y quedando sin memoria). Sí, evite la cadena. Usar string.Split puede resultar en el uso del Montón de objetos grandes (LOH), pero no estoy 100% seguro de eso.
Peter Mortensen
Si hizo SplitToLines un método estático (que parece que dd), entonces ¿cómo puede hacer, blah.SplitToLines.. por ejemplo document.SplitToLines...?
barlop
Ah, veo que pones thislos parámetros formales convirtiéndolo en un método de extensión.
barlop
26

Según la respuesta de Guffa, en una clase de extensión, use:

public static string[] Lines(this string source) {
    return source.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.None);
}
Erwin Mayer
fuente
9

Para una variable de cadena s:

s.Split(new string[]{Environment.NewLine},StringSplitOptions.None)

Esto utiliza la definición de su entorno de terminaciones de línea. En Windows, las terminaciones de línea son CR-LF (retorno de carro, avance de línea) o en los caracteres de escape de C # \r\n.

Esta es una solución confiable, porque si recombinas las líneas con String.Join, esto es igual a tu cadena original:

var lines = s.Split(new string[]{Environment.NewLine},StringSplitOptions.None);
var reconstituted = String.Join(Environment.NewLine,lines);
Debug.Assert(s==reconstituted);

Qué no hacer:

  • Use StringSplitOptions.RemoveEmptyEntries, porque esto romperá el marcado, como Markdown, donde las líneas vacías tienen un propósito sintáctico.
  • Dividir en separador new char[]{Environment.NewLine}, porque en Windows esto creará un elemento de cadena vacío para cada nueva línea.
Coronel Panic
fuente
Básicamente, la misma respuesta aquí que la mejor calificada, la aceptada, pero tiene una buena prueba de unidad y advertencias.
vapcguy
8

Regex también es una opción:

    private string[] SplitStringByLineFeed(string inpString)
    {
        string[] locResult = Regex.Split(inpString, "[\r\n]+");
        return locResult;
    }
usuario1964822
fuente
77
Si desea hacer coincidir las líneas exactamente, conservando las líneas en blanco, esta expresión regular sería mejor: "\r?\n".
Rory O'Kane
7

Solo pensé que agregaría mis dos bits, porque las otras soluciones en esta pregunta no entran en la clasificación de código reutilizable y no son convenientes.

El siguiente bloque de código extiende el stringobjeto para que esté disponible como método natural cuando se trabaja con cadenas.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections;
using System.Collections.ObjectModel;

namespace System
{
    public static class StringExtensions
    {
        public static string[] Split(this string s, string delimiter, StringSplitOptions options = StringSplitOptions.None)
        {
            return s.Split(new string[] { delimiter }, options);
        }
    }
}

Ahora puede usar la .Split()función desde cualquier cadena de la siguiente manera:

string[] result;

// Pass a string, and the delimiter
result = string.Split("My simple string", " ");

// Split an existing string by delimiter only
string foo = "my - string - i - want - split";
result = foo.Split("-");

// You can even pass the split options parameter. When omitted it is
// set to StringSplitOptions.None
result = foo.Split("-", StringSplitOptions.RemoveEmptyEntries);

Para dividir en un carácter de nueva línea, simplemente pase "\n"o "\r\n"como el parámetro delimitador.

Comentario: Sería bueno si Microsoft implementara esta sobrecarga.

Kraang Prime
fuente
Environment.Newlinese prefiere a la codificación rígida \no \r\n.
Michael Blackburn
3
@MichaelBlackburn: esa es una declaración no válida porque no hay contexto. Environment.Newlinees para compatibilidad multiplataforma, no para trabajar con archivos que utilizan diferentes terminaciones de línea que el sistema operativo actual. Consulte aquí para obtener más información , por lo que realmente depende de con qué esté trabajando el desarrollador. El uso de Environment.Newlineasegura que no haya consistencia en el tipo de retorno de línea entre los sistemas operativos, donde la 'codificación rígida' le da al desarrollador un control total.
Kraang Prime
2
@MichaelBlackburn: no es necesario que seas grosero. Simplemente estaba proporcionando la información. .Newlineno es mágico, debajo del capó son solo las cuerdas según lo dispuesto anteriormente en función de un interruptor de si se está ejecutando en Unix o en Windows. La apuesta más segura es hacer primero un reemplazo de cadena para todos "\ r \ n" y luego dividir en "\ n". Cuando el uso .Newlinefalla, es cuando está trabajando con archivos guardados por otros programas que utilizan un método diferente para los saltos de línea. Funciona bien si sabe cada vez que la lectura del archivo siempre usa los saltos de línea de su sistema operativo actual.
Kraang Prime
Entonces, lo que escucho es la forma más legible (tal vez un mayor uso de memoria) foo = foo.Replace("\r\n", "\n"); string[] result = foo.Split('\n');. ¿Entiendo correctamente que esto funciona en todas las plataformas?
John Doe
4

Actualmente estoy usando esta función (basada en otras respuestas) en VB.NET:

Private Shared Function SplitLines(text As String) As String()
    Return text.Split({Environment.NewLine, vbCrLf, vbLf}, StringSplitOptions.None)
End Function

Primero intenta dividirse en la nueva línea local de plataforma y luego recurre a cada nueva línea posible.

Solo he necesitado esto dentro de una clase hasta ahora. Si eso cambia, probablemente haré estoPublic y lo moveré a una clase de utilidad, y tal vez incluso lo convierta en un método de extensión.

Aquí le mostramos cómo volver a unir las líneas, por si acaso:

Private Shared Function JoinLines(lines As IEnumerable(Of String)) As String
    Return String.Join(Environment.NewLine, lines)
End Function
Rory O'Kane
fuente
@Samuel: tenga en cuenta las citas. En realidad tienen ese significado. "\r"= volver. "\r\n"= retorno + nueva línea. (revise esta publicación y la solución aceptada aquí
Kraang Prime
@Kraang Hmm .. No he trabajado con .NET en mucho tiempo. Me sorprendería que tanta gente votara una respuesta incorrecta. Veo que también comenté la respuesta de Guffa y obtuve una aclaración allí. He eliminado mi comentario a esta respuesta. Gracias por el aviso.
Samuel
2

Bueno, en realidad dividir debería hacer:

//Constructing string...
StringBuilder sb = new StringBuilder();
sb.AppendLine("first line");
sb.AppendLine("second line");
sb.AppendLine("third line");
string s = sb.ToString();
Console.WriteLine(s);

//Splitting multiline string into separate lines
string[] splitted = s.Split(new string[] {System.Environment.NewLine}, StringSplitOptions.RemoveEmptyEntries);

// Output (separate lines)
for( int i = 0; i < splitted.Count(); i++ )
{
    Console.WriteLine("{0}: {1}", i, splitted[i]);
}
MaciekTalaska
fuente
2
La opción RemoveEmptyEntries eliminará líneas vacías del texto. Eso puede ser deseable en algunas situaciones, pero una división simple debería preservar las líneas vacías.
Guffa
sí, tienes razón, acabo de suponer que ... bueno, las líneas en blanco no son interesantes;)
MaciekTalaska
1
string[] lines = text.Split(
  Environment.NewLine.ToCharArray(), 
  StringSplitOptions.RemoveEmptyStrings);

La opción RemoveEmptyStrings se asegurará de que no tenga entradas vacías debido a \ n después de un \ r

(Editar para reflejar los comentarios :) Tenga en cuenta que también descartará líneas vacías genuinas en el texto. Esto suele ser lo que quiero, pero puede que no sea su requisito.

Serge Wautier
fuente
Las opciones RemoveEmptyStrings también eliminarán líneas vacías, por lo que no funciona correctamente si el texto tiene líneas vacías.
Guffa
Probablemente desee conservar líneas vacías genuinas: \ r \ n \ r \ n
delgado
0

No sabía sobre Medio Ambiente. Nueva línea, pero supongo que esta es una muy buena solución.

Mi intento hubiera sido:

        string str = "Test Me\r\nTest Me\nTest Me";
        var splitted = str.Split('\n').Select(s => s.Trim()).ToArray();

El .Trim adicional elimina cualquier \ r o \ n que aún pueda estar presente (por ejemplo, en Windows pero dividiendo una cadena con caracteres os x nueva línea). Sin embargo, probablemente no sea el método más rápido.

EDITAR:

Como los comentarios señalaron correctamente, esto también elimina cualquier espacio en blanco al comienzo de la línea o antes del nuevo avance de línea. Si necesita preservar ese espacio en blanco, use una de las otras opciones.

Max
fuente
El recorte también eliminará cualquier espacio en blanco al principio y al final de las líneas, por ejemplo, la sangría.
Guffa el
".Trim elimina cualquier \ r o \ n que aún pueda estar presente" - ouch. ¿Por qué no escribir código robusto en su lugar?
bzlm
Tal vez me equivoqué de pregunta, pero estaba / no está claro que ese espacio en blanco debe ser preservado. Por supuesto que tienes razón, Trim () también elimina los espacios en blanco.
Max
1
@Max: Wow, espera hasta que le diga a mi jefe que el código puede hacer cualquier cosa que no esté específicamente excluida en la especificación ...;)
Guffa
-2

Respuesta tonta: escribe en un archivo temporal para que puedas usar el venerable File.ReadLines

var s = "Hello\r\nWorld";
var path = Path.GetTempFileName();
using (var writer = new StreamWriter(path))
{
    writer.Write(s);
}
var lines = File.ReadLines(path);
Coronel Panic
fuente
1
Evite var, ya que no define el tipo de variable, por lo que es posible que no entienda cómo usar ese objeto o qué representa ese objeto. Además, esto muestra escribir las líneas y ni siquiera especifica un nombre de archivo, por lo que dudo que funcione. Luego, al leer, la ruta al archivo nuevamente no se especifica. Suponiendo que pathsea ​​así C:\Temp\test.txt, entonces debería haberlo hecho string[] lines = File.ReadLines(path);.
vapcguy
1
@vapcguy, ¿qué acabo de leer? - Recomiendo volver a leer la publicación o depurarla en un programa de consola porque todo lo que dijiste es completamente incorrecto | ruta se establece en Path.GetTempFileName | var es una definición común y recomendada en C #: por cierto, define el tipo de una variable ... EDITAR: No digo que sea una buena solución
koanbock el
@koanbock Ok, entonces busqué Path.GetTempFileName msdn.microsoft.com/en-us/library/… y dice que crea un archivo de cero bytes y devuelve "la ruta completa de ese archivo". Podría jurar que probé esto antes y me dio una excepción porque no encontró un archivo, sino que me devolvió una ubicación de carpeta. Conozco los argumentos para usar var, pero diría que NO se recomienda porque no muestra cuál es el objeto variable. Lo ofusca.
vapcguy
-3
using System.IO;

string textToSplit;

if (textToSplit != null)
{
    List<string> lines = new List<string>();
    using (StringReader reader = new StringReader(textToSplit))
    {
        for (string line = reader.ReadLine(); line != null; line = reader.ReadLine())
        {
            lines.Add(line);
        }
    }
}
maciej
fuente
-5

Muy fácil, en realidad.

VB.NET:

Private Function SplitOnNewLine(input as String) As String
    Return input.Split(Environment.NewLine)
End Function

C#:

string splitOnNewLine(string input)
{
    return input.split(environment.newline);
}
Skillaura13
fuente
44
Totalmente incorrecto y no funciona. Además, en C #, es Environment.NewLinecomo en VB.
vapcguy
¿Ver el identificador de fin de línea en VB.NET? para las diferentes opciones de nueva línea.
Peter Mortensen