A maneira mais fácil de dividir uma string em novas linhas no .NET?

806

Eu preciso dividir uma seqüência de caracteres em novas linhas no .NET e a única maneira que eu sei de dividir seqüências é com o método Split . No entanto, isso não me permitirá (facilmente) dividir em uma nova linha; então, qual é a melhor maneira de fazer isso?

RCIX
fonte
2
Por que não? Apenas dividir em System.Environment.NewLine
aviraldg
16
Mas você precisa envolvê-lo em uma string [] e adicionar um argumento extra e ... parece desajeitado.
RCIX 10/10/09

Respostas:

1414

Para dividir em uma string, você precisa usar a sobrecarga que requer uma matriz de strings:

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

Editar:
se você quiser lidar com diferentes tipos de quebras de linha em um texto, poderá usar a capacidade de corresponder a mais de uma sequência. Isso será dividido corretamente em qualquer tipo de quebra de linha e preservará linhas e espaçamento vazios no texto:

string[] lines = theText.Split(
    new[] { "\r\n", "\r", "\n" },
    StringSplitOptions.None
);
Guffa
fonte
3
@RCIX: Enviar os parâmetros corretos para o método é um pouco estranho, porque você o está usando para algo que é muito mais simples do que é capaz. Pelo menos ele está lá, antes do quadro 2, você tinha que usar uma expressão regular ou construir sua própria rotina de divisão para divisão em uma corda ...
Guffa
4
@Leandro: A Environment.NewLinepropriedade contém a nova linha padrão para o sistema. Para um sistema Windows, por exemplo, será "\r\n".
Guffa
3
@Leandro: Um palpite seria que o programa se divide em \ndeixar um \rno final de cada linha e, em seguida, gera as linhas com um \r\nentre eles.
Guffa
3
@ Samuel: As seqüências \re \nescape (entre outras) têm um significado especial para o compilador C #. O VB não possui essas seqüências de escape; portanto, essas constantes são usadas.
Guffa
2
Se você deseja aceitar arquivos de vários sistemas operacionais, também pode adicionar "\ n \ r" ao início e "\ r" ao final da lista de delimitadores. Não tenho certeza se vale a pena o desempenho atingido. ( en.wikipedia.org/wiki/Newline )
user420667
121

Que tal usar um StringReader?

using (System.IO.StringReader reader = new System.IO.StringReader(input)) {
    string line = reader.ReadLine();
}
Clemente
fonte
13
Esse é meu favorito. I enrolada em um método de extensão e linha de corrente de retorno rendimento: gist.github.com/ronnieoverby/7916886
Ronnie Overby
3
Esta é a única solução não-regex que encontrei para .netcf 3.5
Carl
8
Especialmente agradável quando a entrada é grande e copiá-la para uma matriz torna-se lenta / com muita memória.
Alejandro
1
Como está escrito, esta resposta lê apenas a primeira linha. Veja a resposta de Steve Cooper para o whileloop que deve ser adicionado a essa resposta.
ToolmakerSteve
48

Você deve poder dividir sua string com muita facilidade, assim:

aString.Split(Environment.NewLine.ToCharArray());
nikmd23
fonte
46
Em um sistema não * * nix que será dividido nos caracteres separados na sequência Newline, ou seja, nos caracteres CR e LF. Isso causará uma seqüência vazia extra entre cada linha.
Guffa 10/10/09
Me corrija se eu estiver errado, mas isso não será dividido nos caracteres \ en?
RCIX 10/10/09
7
@RCIX: Não, os códigos \ re \ n representam caracteres únicos. A cadeia "\ r \ n" tem dois caracteres, não quatro.
Guffa 10/10/09
10
se você adicionar o parâmetro StringSplitOptions.RemoveEmptyEntries, isso funcionará perfeitamente.
Ruben
18
@ Ruben: Não, não vai. Serge já sugeriu isso em sua resposta, e eu já expliquei que ele também removerá as linhas vazias no texto original que devem ser preservadas.
Guffa
34

Tente evitar o uso de string.Split para uma solução geral, porque você usará mais memória em todos os lugares em que usar a função - a string original e a cópia dividida, ambas na memória. Confie em mim que isso pode ser um problema enorme quando você começa a escalar - execute um aplicativo de processamento em lote de 32 bits que processa documentos de 100 MB e você terá a mínima ideia de oito threads simultâneos. Não que eu já estive lá antes ...

Em vez disso, use um 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;
            }
        }
    }

Isso permitirá que você faça um loop mais eficiente de memória em torno de seus dados;

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

Obviamente, se você quiser tudo na memória, poderá fazer isso;

var allTheLines = document.SplitToLines.ToArray();
Steve Cooper
fonte
Eu estive lá ... (analisando grandes arquivos HTML e ficando sem memória). Sim, evite string.Split. Usar string.Split pode resultar no uso do Large Object Heap (LOH) - mas não tenho 100% de certeza disso.
Peter Mortensen
Se você fez do SplitToLines um método estático (que parece ser o seu caso), como você pode, blah.SplitToLines.. por exemplo document.SplitToLines...?
barlop
ah eu vejo você colocar thisos parâmetros formais, tornando-o um método de extensão.
barlop
26

Com base na resposta de Guffa, em uma classe de extensão, use:

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

Para uma variável de sequência s:

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

Isso usa a definição do seu ambiente de terminações de linha. No Windows, as terminações de linha são CR-LF (retorno de carro, avanço de linha) ou nos caracteres de escape do C # \r\n.

Esta é uma solução confiável, porque se você recombinar as linhas String.Join, isso será igual à sua string original:

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

O que não fazer:

  • Use StringSplitOptions.RemoveEmptyEntries, pois isso interromperá a marcação, como Markdown, onde as linhas vazias têm um propósito sintático.
  • Dividir no separador new char[]{Environment.NewLine}, porque no Windows isso criará um elemento de sequência vazio para cada nova linha.
Coronel Panic
fonte
Basicamente, a mesma resposta aqui como a mais votada, aceita, mas ela possui um bom teste de unidade e ressalvas.
vapcguy
8

Regex também é uma opção:

    private string[] SplitStringByLineFeed(string inpString)
    {
        string[] locResult = Regex.Split(inpString, "[\r\n]+");
        return locResult;
    }
user1964822
fonte
7
Se você deseja corresponder linhas exatamente, preservando as linhas em branco, esta string regex seria melhor: "\r?\n".
Rory O'Kane
7

Eu apenas pensei em adicionar meus dois bits, porque as outras soluções nessa questão não se enquadram na classificação de código reutilizável e não são convenientes.

O seguinte bloco de código estende o stringobjeto para que ele esteja disponível como um método natural ao trabalhar com seqüências de caracteres.

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

Agora você pode usar a .Split()função de qualquer sequência da seguinte maneira:

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 um caractere de nova linha, basta passar "\n"ou "\r\n"como o parâmetro delimitador.

Comentário: Seria bom se a Microsoft implementasse essa sobrecarga.

Kraang Prime
fonte
Environment.Newlineé preferível à codificação codificada \nou \r\n.
22618 Michael Michael Burnburn
3
@ MichaelBlackburn - Essa é uma declaração inválida porque não há contexto. Environment.Newlineé para compatibilidade de plataforma cruzada, não para trabalhar com arquivos que usam terminações de linha diferentes do sistema operacional atual. Veja aqui para mais informações , portanto depende realmente do que o desenvolvedor está trabalhando. O uso de Environment.Newlinegarante que não haja consistência no tipo de retorno de linha entre os sistemas operacionais, onde a 'codificação permanente' oferece ao desenvolvedor controle total.
Kraang Prime
2
@ MichaelBlackburn - Não há necessidade de você ser rude. Eu estava apenas fornecendo as informações. .Newlinenão é mágico, sob o capô são apenas as seqüências de caracteres fornecidas acima, com base em uma opção de se ele está sendo executado no unix ou no windows. A aposta mais segura é primeiro substituir uma string por todos "\ r \ n" e depois dividir em "\ n". Onde o uso .Newlinefalha, é quando você está trabalhando com arquivos salvos por outros programas que usam um método diferente para quebras de linha. Funciona bem se você souber sempre que o arquivo lido estiver sempre usando as quebras de linha do seu sistema operacional atual.
Kraang Prime
Então, o que estou ouvindo é a maneira mais legível (talvez maior uso de memória) foo = foo.Replace("\r\n", "\n"); string[] result = foo.Split('\n');. Estou entendendo corretamente que isso funciona em todas as plataformas?
John Doe
4

Atualmente, estou usando esta função (com base em outras respostas) no VB.NET:

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

Ele tenta dividir primeiro a nova linha local da plataforma e depois recair em cada nova linha possível.

Eu só precisava disso dentro de uma classe até agora. Se isso mudar, provavelmente vou fazer isso Publice movê-lo para uma classe de utilitário, e talvez até torná-lo um método de extensão.

Veja como juntar as linhas de volta, para uma boa medida:

Private Shared Function JoinLines(lines As IEnumerable(Of String)) As String
    Return String.Join(Environment.NewLine, lines)
End Function
Rory O'Kane
fonte
@ Samuel - observe as citações. Eles realmente têm esse significado. "\r"= retorno. "\r\n"= retorno + nova linha. (revise este post e a solução aceita aqui
Kraang Prime 18/04/19
@ Kraang Hmm .. Eu não trabalho com .NET há muito tempo. Eu ficaria surpreso se tantas pessoas votassem em uma resposta errada. Vejo que também comentei a resposta de Guffa e recebi esclarecimentos. Excluí meu comentário para esta resposta. Obrigado pela atenção.
19718 Samuel
2

Bem, na verdade, a divisão deve fazer:

//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
fonte
2
A opção RemoveEmptyEntries removerá as linhas vazias do texto. Isso pode ser desejável em algumas situações, mas uma divisão simples deve preservar as linhas vazias.
Guffa
sim, você está certo, eu só fiz essa suposição, que ... bem, as linhas em branco não são interessantes;)
MaciekTalaska
1
string[] lines = text.Split(
  Environment.NewLine.ToCharArray(), 
  StringSplitOptions.RemoveEmptyStrings);

A opção RemoveEmptyStrings garantirá que você não tenha entradas vazias devido a \ n após um \ r

(Edite para refletir os comentários :) Observe que ele também descartará linhas vazias genuínas no texto. Geralmente é isso que eu quero, mas pode não ser sua exigência.

Serge Wautier
fonte
As opções RemoveEmptyStrings também removerão linhas vazias; portanto, não funcionará corretamente se o texto contiver linhas vazias.
Guffa 10/10/09
Você provavelmente quer preservar linhas vazias genuínos: \ r \ n \ r \ n
magro
0

Eu não sabia sobre o Environment.Newline, mas acho que essa é uma solução muito boa.

Minha tentativa teria sido:

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

O .Trim adicional remove qualquer \ r ou \ n que ainda possa estar presente (por exemplo, quando estiver no Windows, mas dividindo uma string com caracteres de nova linha os x). Provavelmente não é o método mais rápido.

EDITAR:

Como os comentários apontaram corretamente, isso também remove qualquer espaço em branco no início da linha ou antes do novo avanço de linha. Se você precisar preservar esse espaço em branco, use uma das outras opções.

Máx.
fonte
O Trim também removerá qualquer espaço em branco no início e no final das linhas, por exemplo, recuo.
Guffa 10/10/09
".Trim remove qualquer \ r ou \ n que ainda possa estar presente" - ai. Por que não escrever um código robusto?
bzlm
Talvez eu tenha entendido errado a pergunta, mas não estava claro se esse espaço em branco deve ser preservado. Claro que você está certo, Trim () também remove os espaços em branco.
Max
1
@Max: Uau, esperar até que eu dizer ao meu chefe que o código é permitido fazer qualquer coisa que não esteja especificamente descartada na especificação ...;)
Guffa
-2

Resposta boba: escreva em um arquivo temporário para poder usar o venerável 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
fonte
1
Evite var, pois ele não define o tipo de variável, portanto você pode não entender como usar esse objeto ou o que esse objeto representa. Além disso, isso mostra a escrita das linhas e nem sequer especifica um nome de arquivo, então duvido que funcione. Em seguida, ao ler, o caminho para o arquivo novamente não é especificado. Supondo que pathseja C:\Temp\test.txt, você deve ter string[] lines = File.ReadLines(path);.
vapcguy
1
@vapcguy o que acabei de ler? - Eu recomendaria reler a postagem ou depurá-la em um programa de console, porque tudo o que você disse está completamente errado | caminho é definido em Path.GetTempFileName | var é um comum e definição recomendada em C # - pela forma como se define o tipo de uma variável ...... EDIT: Eu não digo que esta é uma boa solução
koanbock
@koanbock Ok, então procurei Path.GetTempFileName msdn.microsoft.com/en-us/library/… e ele diz que cria um arquivo de zero byte e retorna "o caminho completo desse arquivo". Eu poderia jurar que tentei isso antes e isso deu uma exceção porque não encontrou um arquivo, mas foi retornada uma localização de pasta. Conheço os argumentos para usar var, mas diria que NÃO é recomendado porque não mostra qual é o objeto variável. O 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
fonte
-5

Muito fácil, na verdade.

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
fonte
4
Totalmente incorreto e não funciona. Além disso, em C #, é Environment.NewLinecomo no VB.
vapcguy
Consulte Identificador de fim de linha no VB.NET? para as diferentes opções de nova linha.
Peter Mortensen