Der einfachste Weg, eine Zeichenfolge in Zeilenumbrüchen in .NET zu teilen?

806

Ich muss eine Zeichenfolge in .NET in Zeilenumbrüche aufteilen. Die einzige Möglichkeit, Zeichenfolgen aufzuteilen, ist die Split- Methode. Dadurch kann ich mich jedoch nicht (leicht) in eine neue Zeile aufteilen. Was ist also der beste Weg, dies zu tun?

RCIX
quelle
2
Warum sollte es nicht? Einfach auf System.Environment.NewLine aufteilen
aviraldg
16
Aber Sie müssen es in eine Zeichenfolge [] einschließen und ein zusätzliches Argument hinzufügen, und ... es fühlt sich einfach klobig an.
RCIX

Antworten:

1413

Um einen String zu teilen, müssen Sie die Überladung verwenden, die ein Array von Strings benötigt:

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

Bearbeiten:
Wenn Sie verschiedene Arten von Zeilenumbrüchen in einem Text verarbeiten möchten, können Sie die Möglichkeit verwenden, mehr als eine Zeichenfolge abzugleichen. Dadurch werden beide Zeilenumbrüche korrekt aufgeteilt und leere Zeilen und Abstände im Text beibehalten:

string[] lines = theText.Split(
    new[] { "\r\n", "\r", "\n" },
    StringSplitOptions.None
);
Guffa
quelle
3
@RCIX: Das Senden der richtigen Parameter an die Methode ist etwas umständlich, da Sie sie für etwas verwenden, das viel einfacher ist als das, was es kann. Zumindest war es da, vor Framework 2 musste man einen regulären Ausdruck verwenden oder eine eigene Aufteilungsroutine erstellen, um einen String aufzuteilen ...
Guffa
4
@Leandro: Die Environment.NewLineEigenschaft enthält den Standard-Zeilenumbruch für das System. Für ein Windows-System wird es zum Beispiel sein "\r\n".
Guffa
3
@Leandro: Eine Vermutung wäre, dass sich das Programm beim \nVerlassen eines \ram Ende jeder Zeile aufteilt und dann die Zeilen mit einem \r\ndazwischen ausgibt .
Guffa
3
@Samuel: Die Sequenzen \rund \nEscape (unter anderem) haben für den C # -Compiler eine besondere Bedeutung. VB hat diese Escape-Sequenzen nicht, daher werden stattdessen diese Konstanten verwendet.
Guffa
2
Wenn Sie Dateien von vielen verschiedenen Betriebssystemen akzeptieren möchten, können Sie am Anfang auch "\ n \ r" und am Ende der Trennzeichenliste "\ r" hinzufügen. Ich bin mir nicht sicher, ob es den Performance-Hit wert ist. ( en.wikipedia.org/wiki/Newline )
user420667
121

Was ist mit einem StringReader?

using (System.IO.StringReader reader = new System.IO.StringReader(input)) {
    string line = reader.ReadLine();
}
Clément
quelle
13
Das ist mein Lieblings. Ich wickelte mich in eine Erweiterungsmethode ein und gab die aktuelle Zeile zurück: gist.github.com/ronnieoverby/7916886
Ronnie Overby
3
Dies ist die einzige Nicht-Regex-Lösung, die ich für .netcf 3.5
Carl
8
Besonders schön, wenn die Eingabe groß ist und das Kopieren in ein Array langsam / speicherintensiv wird.
Alejandro
1
Wie geschrieben, liest diese Antwort nur die erste Zeile. Siehe Steve Coopers Antwort für die whileSchleife, die zu dieser Antwort hinzugefügt werden sollte.
ToolmakerSteve
48

Sie sollten in der Lage sein, Ihre Saite ziemlich einfach zu teilen, wie folgt:

aString.Split(Environment.NewLine.ToCharArray());
nikmd23
quelle
46
Auf einem Nicht-Nix-System, das auf die einzelnen Zeichen in der Newline-Zeichenfolge aufgeteilt wird, dh die CR- und LF-Zeichen. Dies führt zu einer zusätzlichen leeren Zeichenfolge zwischen den einzelnen Zeilen.
Guffa
Korrigieren Sie mich, wenn ich falsch liege, aber wird das nicht auf die Zeichen \ und n aufgeteilt?
RCIX
7
@RCIX: Nein, die Codes \ r und \ n stehen für einzelne Zeichen. Die Zeichenfolge "\ r \ n" besteht aus zwei Zeichen, nicht aus vier.
Guffa
10
Wenn Sie den Parameter StringSplitOptions.RemoveEmptyEntries hinzufügen, funktioniert dies einwandfrei.
Ruben
18
@ Ruben: Nein, das wird es nicht. Serge hat dies bereits in seiner Antwort vorgeschlagen, und ich habe bereits erklärt, dass dadurch auch die leeren Zeilen im Originaltext entfernt werden, die beibehalten werden sollen.
Guffa
34

Vermeiden Sie die Verwendung von string.Split für eine allgemeine Lösung, da Sie überall dort, wo Sie die Funktion verwenden, mehr Speicher verwenden - die ursprüngliche Zeichenfolge und die geteilte Kopie, beide im Speicher. Vertrauen Sie mir, dass dies ein verdammt großes Problem sein kann, wenn Sie mit der Skalierung beginnen. Führen Sie eine 32-Bit-Stapelverarbeitungs-App aus, die 100-MB-Dokumente verarbeitet, und Sie werden auf acht gleichzeitige Threads verzichten. Nicht dass ich schon einmal dort gewesen wäre ...

Verwenden Sie stattdessen einen solchen Iterator.

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

Auf diese Weise können Sie eine speichereffizientere Schleife um Ihre Daten durchführen.

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

Wenn Sie alles im Speicher haben möchten, können Sie dies natürlich tun.

var allTheLines = document.SplitToLines.ToArray();
Steve Cooper
quelle
Ich war dort ... (Analyse großer HTML-Dateien und zu wenig Speicher). Ja, vermeiden Sie string.Split. Die Verwendung von string.Split kann zur Verwendung des Large Object Heap (LOH) führen - da bin ich mir jedoch nicht 100% sicher.
Peter Mortensen
Wenn Sie SplitToLines zu einer statischen Methode gemacht haben (was Sie anscheinend getan haben), wie können Sie dann blah.SplitToLines.. z document.SplitToLines....
Barlop
ah, ich sehe, Sie geben thisdie formalen Parameter ein, was es zu einer Erweiterungsmethode macht.
Barlop
26

Verwenden Sie basierend auf Guffas Antwort in einer Erweiterungsklasse:

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

Für eine Zeichenfolgenvariable s:

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

Dies verwendet die Definition der Zeilenenden in Ihrer Umgebung. Unter Windows sind die Zeilenenden CR-LF (Wagenrücklauf, Zeilenvorschub) oder die Escapezeichen von C # \r\n.

Dies ist eine zuverlässige Lösung, denn wenn Sie die Zeilen mit neu kombinieren String.Join, entspricht dies Ihrer ursprünglichen Zeichenfolge:

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

Was nicht zu tun ist:

  • Verwenden Sie StringSplitOptions.RemoveEmptyEntriesdiese Option , da dadurch Markups wie Markdown unterbrochen werden, bei denen leere Zeilen einen syntaktischen Zweck haben.
  • Auf Trennzeichen teilen new char[]{Environment.NewLine}, da unter Windows ein leeres Zeichenfolgenelement für jede neue Zeile erstellt wird.
Oberst Panik
quelle
Grundsätzlich die gleiche Antwort wie die am besten bewertete, akzeptierte, aber es gibt einen schönen Unit-Test und Vorbehalte.
Vapcguy
8

Regex ist auch eine Option:

    private string[] SplitStringByLineFeed(string inpString)
    {
        string[] locResult = Regex.Split(inpString, "[\r\n]+");
        return locResult;
    }
user1964822
quelle
7
Wenn Sie Zeilen genau zuordnen möchten, wobei Leerzeilen beibehalten werden sollen, ist diese Regex-Zeichenfolge besser geeignet : "\r?\n".
Rory O'Kane
7

Ich dachte nur, ich würde meine zwei Bits hinzufügen, weil die anderen Lösungen für diese Frage nicht in die wiederverwendbare Codeklassifizierung fallen und nicht bequem sind.

Der folgende Codeblock erweitert das stringObjekt so, dass es als natürliche Methode beim Arbeiten mit Zeichenfolgen verfügbar ist.

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

Sie können die .Split()Funktion jetzt aus einer beliebigen Zeichenfolge wie folgt verwenden:

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

Um ein Zeilenumbruchzeichen aufzuteilen, übergeben Sie einfach "\n"oder "\r\n"als Begrenzerparameter.

Kommentar: Es wäre schön, wenn Microsoft diese Überlastung implementieren würde.

Kraang Prime
quelle
Environment.Newlineist der harten Codierung entweder \noder vorzuziehen \r\n.
Michael Blackburn
3
@MichaelBlackburn - Das ist eine ungültige Aussage, da es keinen Kontext gibt. Environment.Newlinedient der plattformübergreifenden Kompatibilität und nicht der Arbeit mit Dateien, die andere Leitungsabschlüsse als das aktuelle Betriebssystem verwenden. Weitere Informationen finden Sie hier. Es hängt also wirklich davon ab, mit was der Entwickler arbeitet. Durch die Verwendung von wird Environment.Newlinesichergestellt, dass der Zeilenrückgabetyp zwischen den Betriebssystemen nicht konsistent ist. Durch die Hardcodierung erhält der Entwickler die volle Kontrolle.
Kraang Prime
2
@ MichaelBlackburn - Sie müssen nicht unhöflich sein. Ich habe lediglich die Informationen bereitgestellt. .Newlineist keine Zauberei, unter der Haube sind es nur die oben angegebenen Zeichenfolgen, basierend auf einem Schalter, ob es unter Unix oder unter Windows läuft. Am sichersten ist es, zuerst einen String-Ersatz für alle "\ r \ n" durchzuführen und dann auf "\ n" aufzuteilen. Wenn die Verwendung .Newlinefehlschlägt, arbeiten Sie mit Dateien, die von anderen Programmen gespeichert wurden, die eine andere Methode für Zeilenumbrüche verwenden. Es funktioniert gut, wenn Sie wissen, dass beim Lesen der Datei immer die Zeilenumbrüche Ihres aktuellen Betriebssystems verwendet werden.
Kraang Prime
Was ich höre, ist der am besten lesbare Weg (möglicherweise eine höhere Speichernutzung) foo = foo.Replace("\r\n", "\n"); string[] result = foo.Split('\n');. Verstehe ich richtig, dass dies auf allen Plattformen funktioniert?
John Doe
4

Ich verwende derzeit diese Funktion (basierend auf anderen Antworten) in VB.NET:

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

Es wird versucht, zuerst die plattformlokale Newline aufzuteilen und dann auf jede mögliche Newline zurückzugreifen.

Ich habe das bisher nur in einer Klasse gebraucht. Wenn sich das ändert, werde ich dies wahrscheinlich machen Publicund es in eine Utility-Klasse verschieben und es vielleicht sogar zu einer Erweiterungsmethode machen.

Hier erfahren Sie, wie Sie die Zeilen wieder zusammenfügen können:

Private Shared Function JoinLines(lines As IEnumerable(Of String)) As String
    Return String.Join(Environment.NewLine, lines)
End Function
Rory O'Kane
quelle
@ Samuel - beachte die Zitate. Sie haben tatsächlich diese Bedeutung. "\r"= zurück. "\r\n"= return + neue Zeile. (Bitte überprüfen Sie diesen Beitrag und die akzeptierte Lösung hier
Kraang Prime
@ Kraang Hmm .. Ich habe lange nicht mehr mit .NET gearbeitet. Ich wäre überrascht, wenn so viele Leute eine falsche Antwort gewählt hätten. Ich sehe, dass ich auch Guffas Antwort kommentiert und dort Klarheit bekommen habe. Ich habe meinen Kommentar zu dieser Antwort gelöscht. Danke für die Warnung.
Samuel
2

Nun, eigentlich sollte Split reichen:

//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
quelle
2
Mit der Option RemoveEmptyEntries werden leere Zeilen aus dem Text entfernt. Dies kann in einigen Situationen wünschenswert sein, aber eine einfache Aufteilung sollte die leeren Zeilen beibehalten.
Guffa
Ja, du hast recht, ich habe gerade angenommen, dass ... nun, leere Zeilen sind nicht interessant;)
MaciekTalaska
1
string[] lines = text.Split(
  Environment.NewLine.ToCharArray(), 
  StringSplitOptions.RemoveEmptyStrings);

Die Option RemoveEmptyStrings stellt sicher, dass Sie keine leeren Einträge haben, da \ n einem \ r folgt

(Bearbeiten, um Kommentare wiederzugeben :) Beachten Sie, dass auch echte Leerzeilen im Text verworfen werden. Dies ist normalerweise das, was ich möchte, aber es ist möglicherweise nicht Ihre Anforderung.

Serge Wautier
quelle
Mit den Optionen "RemoveEmptyStrings" werden auch leere Zeilen entfernt, sodass dies nicht ordnungsgemäß funktioniert, wenn der Text leere Zeilen enthält.
Guffa
Sie möchten wahrscheinlich echte Leerzeilen beibehalten: \ r \ n \ r \ n
schlank
0

Ich wusste nichts über Environment.Newline, aber ich denke, dies ist eine sehr gute Lösung.

Mein Versuch wäre gewesen:

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

Das zusätzliche .Trim entfernt alle \ r oder \ n, die möglicherweise noch vorhanden sind (z. B. unter Windows, aber Teilen einer Zeichenfolge mit OS x Newline-Zeichen). Wahrscheinlich nicht die schnellste Methode.

BEARBEITEN:

Wie in den Kommentaren richtig hervorgehoben, werden dadurch auch Leerzeichen am Zeilenanfang oder vor dem neuen Zeilenvorschub entfernt. Wenn Sie dieses Leerzeichen beibehalten müssen, verwenden Sie eine der anderen Optionen.

Max
quelle
Durch das Zuschneiden werden auch Leerzeichen am Anfang und Ende von Zeilen entfernt, z. B. Einrückungen.
Guffa
".Trim entfernt alle noch vorhandenen \ r oder \ n" - autsch. Warum nicht stattdessen robusten Code schreiben?
Bzlm
Vielleicht habe ich die Frage falsch verstanden, aber es war / ist nicht klar, dass Leerzeichen erhalten bleiben müssen. Natürlich haben Sie Recht, Trim () entfernt auch Leerzeichen.
Max
1
@Max: Wow, warte, bis ich meinem Chef sage, dass Code alles tun darf, was in der Spezifikation nicht ausdrücklich ausgeschlossen ist ...;)
Guffa
-2

Dumme Antwort: Schreiben Sie in eine temporäre Datei, damit Sie den Ehrwürdigen verwenden können 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);
Oberst Panik
quelle
1
Vermeiden Sie es var, da es den Variablentyp nicht definiert, sodass Sie möglicherweise nicht verstehen, wie dieses Objekt verwendet wird oder was dieses Objekt darstellt. Außerdem zeigt dies das Schreiben der Zeilen und gibt nicht einmal einen Dateinamen an, sodass ich bezweifle, dass dies funktionieren würde. Beim Lesen wird dann der Pfad zur Datei erneut nicht angegeben. Vorausgesetzt, das pathist C:\Temp\test.txt, sollten Sie dann haben string[] lines = File.ReadLines(path);.
Vapcguy
1
@ vapcguy was habe ich gerade gelesen? - Ich würde empfehlen, den Beitrag erneut zu lesen oder in einem Konsolenprogramm zu debuggen, da alles, was Sie gesagt haben, einfach falsch ist Pfad wird auf Path.GetTempFileName | festgelegt var ist eine gebräuchliche und empfohlene Definition in C # - übrigens definiert sie den Typ einer Variablen ...... EDIT: Ich sage nicht, dass dies eine gute Lösung ist
koanbock
@koanbock Ok, also habe ich Path.GetTempFileName msdn.microsoft.com/en-us/library/… nachgeschlagen und es heißt, es wird eine Null-Byte-Datei erstellt und "der vollständige Pfad dieser Datei" zurückgegeben. Ich könnte schwören, dass ich das schon einmal versucht habe und es eine Ausnahme gab, weil es keine Datei gefunden hat, sondern stattdessen einen Ordnerspeicherort zurückgegeben hat. Ich kenne die Argumente für die Verwendung var, aber ich würde sagen, es wird NICHT empfohlen, da es nicht zeigt, was das variable Objekt ist. Es verschleiert es.
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
quelle
-5

Eigentlich sehr einfach.

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
quelle
4
Völlig falsch und funktioniert nicht. Außerdem ist es in C # Environment.NewLinegenau wie in VB.
Vapcguy
Siehe Zeilenende-ID in VB.NET? für die verschiedenen Optionen für neue Leitung.
Peter Mortensen