Il modo più semplice per dividere una stringa su newline in .NET?


806

Ho bisogno di dividere una stringa in newline in .NET e l'unico modo che conosco per dividere le stringhe è con il metodo Split . Tuttavia ciò non mi permetterà di dividere (facilmente) su una nuova riga, quindi qual è il modo migliore per farlo?


2
Perché non dovrebbe? Appena diviso su System.Environment.NewLine
aviraldg

16
Ma devi avvolgerlo in una stringa [] e aggiungere un argomento in più e ... sembra goffo.
RCIX,

Risposte:


1414

Per dividere una stringa è necessario utilizzare il sovraccarico che accetta una matrice di stringhe:

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

Modifica:
se si desidera gestire diversi tipi di interruzioni di riga in un testo, è possibile utilizzare la possibilità di abbinare più di una stringa. Questo si dividerà correttamente su entrambi i tipi di interruzione di riga e manterrà le righe vuote e la spaziatura nel testo:

string[] lines = theText.Split(
    new[] { "\r\n", "\r", "\n" },
    StringSplitOptions.None
);

3
@RCIX: inviare i parametri corretti al metodo è un po 'imbarazzante perché lo stai usando per qualcosa di molto più semplice di quello di cui è capace. Almeno è lì, prima del framework 2
dovevi

4
@Leandro: la Environment.NewLineproprietà contiene la nuova riga predefinita per il sistema. Per un sistema Windows, ad esempio, lo sarà "\r\n".
Guffa,

3
@Leandro: Un'ipotesi sarebbe che il programma si divide \nlasciando una \ralla fine di ogni riga, quindi emette le linee \r\ntra loro.
Guffa,

3
@ Samuel: Le sequenze \re \nescape (tra gli altri) hanno un significato speciale per il compilatore C #. VB non ha quelle sequenze di escape, quindi lì vengono usate quelle costanti.
Guffa,

2
Se si desidera accettare file da molti diversi sistemi operativi, è possibile aggiungere "\ n \ r" all'inizio e "\ r" alla fine dell'elenco dei delimitatori. Non sono sicuro che valga la pena il colpo di prestazione. ( en.wikipedia.org/wiki/Newline )
user420667,

121

Che ne dici di usare un StringReader?

using (System.IO.StringReader reader = new System.IO.StringReader(input)) {
    string line = reader.ReadLine();
}

13
Questo è il mio preferito. Ho inserito
Ronnie Overby l'

3
Questa è l'unica soluzione non regex che ho trovato per .netcf 3.5
Carl,

8
Particolarmente bello quando l'input è grande e copiarlo su un array diventa lento / intensivo di memoria.
Alejandro,

1
Come scritto, questa risposta legge solo la prima riga. Vedi la risposta di Steve Cooper per il whileciclo che dovrebbe essere aggiunto a questa risposta.
ToolmakerSteve

48

Dovresti essere in grado di dividere la stringa abbastanza facilmente, in questo modo:

aString.Split(Environment.NewLine.ToCharArray());

46
Su un sistema non * nix che si dividerà sui caratteri separati nella stringa Newline, ovvero i caratteri CR e LF. Ciò causerà una stringa vuota aggiuntiva tra ogni riga.
Guffa,

Correggimi se sbaglio, ma non si dividerà sui personaggi \ e n?
RCIX,

7
@RCIX: No, i codici \ r e \ n rappresentano singoli caratteri. La stringa "\ r \ n" è composta da due caratteri, non quattro.
Guffa,

10
se aggiungi il parametro StringSplitOptions.RemoveEmptyEntries, funzionerà perfettamente.
Ruben,

18
@ Ruben: No, non lo farà. Serge ha già suggerito che nella sua risposta, e ho già spiegato che rimuoverà anche le righe vuote nel testo originale che dovrebbero essere preservate.
Guffa,

34

Cerca di evitare l'uso della stringa. Spaccati per una soluzione generale, perché utilizzerai più memoria ovunque usi la funzione: la stringa originale e la copia divisa, entrambe in memoria. Fidati di me che questo può essere un grosso problema quando inizi a ridimensionare: esegui un'app di elaborazione batch a 32 bit che elabora documenti da 100 MB e ti ritroverai con otto thread simultanei. Non che ci sia stato prima ...

Invece, usa un iteratore come questo;

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

Ciò ti consentirà di eseguire un ciclo più efficiente della memoria attorno ai tuoi dati;

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

Certo, se vuoi tutto in memoria, puoi farlo;

var allTheLines = document.SplitToLines.ToArray();

Ci sono stato ... (analizzando file HTML di grandi dimensioni ed esaurendo la memoria). Sì, evita string.Split. L'uso di string.Split può comportare l'utilizzo di Large Object Heap (LOH), ma non ne sono sicuro al 100%.
Peter Mortensen,

Se hai reso SplitToLines un metodo statico (che sembra dd), come puoi fare, blah.SplitToLines.. ad esempio document.SplitToLines...?
barlop

ah vedo che hai inserito thisi parametri formali rendendolo un metodo di estensione.
barlop

26

Sulla base della risposta di Guffa, in una classe di estensione, utilizzare:

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

9

Per una variabile stringa s:

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

Questo utilizza la definizione del tuo ambiente di terminazioni di linea. Su Windows, i finali di riga sono CR-LF (ritorno a capo, avanzamento riga) o in caratteri di escape di C # \r\n.

Questa è una soluzione affidabile, perché se ricombini le linee con String.Join, questo equivale alla tua stringa originale:

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

Cosa non fare:

  • Utilizzare StringSplitOptions.RemoveEmptyEntries, perché ciò interromperà il markup come Markdown in cui le righe vuote hanno uno scopo sintattico.
  • Dividi sul separatore new char[]{Environment.NewLine}, perché su Windows questo creerà un elemento stringa vuoto per ogni nuova riga.

Fondamentalmente la stessa risposta qui come quella più votata, accettata, ma ha un bel test unitario e avvertenze.
vapcguy,

8

Regex è anche un'opzione:

    private string[] SplitStringByLineFeed(string inpString)
    {
        string[] locResult = Regex.Split(inpString, "[\r\n]+");
        return locResult;
    }

7
Se si desidera far corrispondere esattamente le linee, preservando righe vuote, questa stringa regex sarebbe meglio: "\r?\n".
Rory O'Kane,

7

Ho solo pensato di aggiungere i miei due bit, perché le altre soluzioni su questa domanda non rientrano nella classificazione riutilizzabile del codice e non sono convenienti.

Il seguente blocco di codice estende l' stringoggetto in modo che sia disponibile come metodo naturale quando si lavora con le stringhe.

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

Ora puoi usare la .Split()funzione da qualsiasi stringa come segue:

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

Per dividere un carattere di nuova riga, è sufficiente passare "\n"o "\r\n"come parametro delimitatore.

Commento: Sarebbe bello se Microsoft implementasse questo sovraccarico.


Environment.Newlineè preferito alla codifica hardware \no \r\n.
Michael Blackburn,

3
@MichaelBlackburn - Questa è un'affermazione non valida perché non c'è contesto. Environment.Newlineserve per la compatibilità multipiattaforma, non per lavorare con file che usano terminazioni di linea diverse rispetto all'attuale sistema operativo. Vedi qui per maggiori informazioni , quindi dipende davvero da cosa sta lavorando lo sviluppatore. L'uso di Environment.Newlinegarantisce che non vi sia coerenza nel tipo di ritorno di riga tra i sistemi operativi, in cui la "codifica rigida" offre allo sviluppatore il pieno controllo.
Kraang Prime,

2
@MichaelBlackburn - Non è necessario che tu sia scortese. Stavo semplicemente fornendo le informazioni. .Newlinenon è magico, sotto il cofano sono solo le stringhe fornite sopra basate su un interruttore di se è in esecuzione su Unix o su Windows. La scommessa più sicura, è prima fare una sostituzione di stringa per tutto "\ r \ n" e poi dividere su "\ n". Dove l'utilizzo .Newlinenon riesce, è quando si lavora con file salvati da altri programmi che utilizzano un metodo diverso per le interruzioni di riga. Funziona bene se lo sai ogni volta che il file letto utilizza sempre le interruzioni di riga del tuo attuale sistema operativo.
Kraang Prime,

Quindi quello che sto ascoltando è il modo più leggibile (forse un uso maggiore della memoria) è foo = foo.Replace("\r\n", "\n"); string[] result = foo.Split('\n');. Comprendo correttamente che funziona su tutte le piattaforme?
John Doe,

4

Attualmente sto usando questa funzione (sulla base di altre risposte) in VB.NET:

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

Tenta di dividere prima la nuova riga locale-piattaforma, quindi ricade su ciascuna possibile nuova riga.

Finora ne ho avuto bisogno solo all'interno di una classe. Se ciò cambia, probabilmente lo farò Publice lo sposterò in una classe di utilità, e forse lo trasformerò anche in un metodo di estensione.

Ecco come unire le linee di backup, per una buona misura:

Private Shared Function JoinLines(lines As IEnumerable(Of String)) As String
    Return String.Join(Environment.NewLine, lines)
End Function

@ Samuel - annota le citazioni. In realtà hanno quel significato. "\r"= ritorno. "\r\n"= return + nuova riga. (rivedi questo post e la soluzione accettata qui
Kraang Prime,

@Kraang Hmm .. Non lavoro con .NET da molto tempo. Sarei sorpreso se molte persone votassero una risposta sbagliata. Vedo che ho commentato anche la risposta di Guffa, e ho ottenuto chiarimenti lì. Ho eliminato il mio commento a questa risposta. Grazie per il testa a testa.
Samuel,

2

Bene, in realtà la divisione dovrebbe fare:

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

2
L'opzione RemoveEmptyEntries rimuoverà le righe vuote dal testo. Ciò può essere desiderabile in alcune situazioni, ma una semplice divisione dovrebbe preservare le linee vuote.
Guffa,

sì, hai ragione, ho appena fatto questo presupposto, che ... beh, le righe vuote non sono interessanti;)
MaciekTalaska

1
string[] lines = text.Split(
  Environment.NewLine.ToCharArray(), 
  StringSplitOptions.RemoveEmptyStrings);

L' opzione RemoveEmptyStrings farà in modo di non avere voci vuote a causa di \ n che segue a \ r

(Modifica per riflettere i commenti :) Nota che eliminerà anche le righe vuote autentiche nel testo. Questo è di solito quello che voglio ma potrebbe non essere il tuo requisito.


Le opzioni RemoveEmptyStrings rimuoveranno anche le righe vuote, quindi non funziona correttamente se il testo contiene righe vuote.
Guffa,

Probabilmente vuoi preservare le vere righe vuote: \ r \ n \ r \ n
slim

0

Non sapevo di Environment.Newline, ma credo che questa sia un'ottima soluzione.

Il mio tentativo sarebbe stato:

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

Il .Trim aggiuntivo rimuove qualsiasi \ r o \ n che potrebbe essere ancora presente (ad es. Su windows ma suddividendo una stringa con caratteri os x newline). Probabilmente non è il metodo più veloce però.

MODIFICARE:

Come sottolineato correttamente dai commenti, questo rimuove anche qualsiasi spazio bianco all'inizio della riga o prima del nuovo feed di riga. Se è necessario preservare quello spazio bianco, utilizzare una delle altre opzioni.


Il Trim rimuoverà anche qualsiasi spazio bianco all'inizio e alla fine delle linee, ad esempio il rientro.
Guffa,

".Trim rimuove qualsiasi \ r o \ n che potrebbe essere ancora presente" - ouch. Perché non scrivere codice robusto invece?
bzlm,

Forse ho sbagliato la domanda, ma era / non è chiaro che gli spazi bianchi debbano essere preservati. Naturalmente hai ragione, Trim () rimuove anche gli spazi bianchi.
Max

1
@Max: Wow, aspetta fino a quando dico al mio capo che al codice è permesso fare qualsiasi cosa che non sia specificatamente esclusa nelle specifiche ...;)
Guffa

-2

Risposta sciocca: scrivi in ​​un file temporaneo in modo da poter usare il venerabile 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);

1
Evita var, poiché non definisce il tipo di variabile, quindi potresti non capire come usare quell'oggetto o cosa rappresenta quell'oggetto. Inoltre, questo mostra la scrittura delle righe e non specifica nemmeno un nome di file, quindi dubito che funzionerebbe. Quindi, durante la lettura, il percorso del file non viene nuovamente specificato. Supponendo che pathsia C:\Temp\test.txt, dovresti avere string[] lines = File.ReadLines(path);.
vapcguy,

1
@vapcguy cosa ho appena letto? - Consiglierei di rileggere il post o eseguirne il debug in un programma della console perché tutto ciò che hai detto è chiaramente sbagliato il percorso è impostato su Path.GetTempFileName | var è una definizione comune e raccomandata in C # - dal modo in cui definisce il tipo di una variabile ...... EDIT: non dico che questa sia una buona soluzione
koanbock

@koanbock Ok, quindi ho cercato Path.GetTempFileName msdn.microsoft.com/en-us/library/… e dice che crea un file a zero byte e restituisce "il percorso completo di quel file". Potrei giurare di averlo provato prima e ha dato un'eccezione perché non ha trovato un file, ma è stato invece restituito un percorso di cartella. Conosco gli argomenti da usare var, ma direi che NON è raccomandato perché non mostra quale sia l'oggetto variabile. Lo offusca.
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);
        }
    }
}

-5

Molto semplice, in realtà.

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

4
Totalmente errato e non funziona. Inoltre, in C #, è Environment.NewLineproprio come in VB.
vapcguy,

Vedi Identificatore di fine riga in VB.NET? per le diverse opzioni per la nuova linea.
Peter Mortensen,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.