Самый простой способ разбить строку на новые строки в .NET?

806

Мне нужно разбить строку на новые строки в .NET, и единственный известный мне способ разбить строки - это метод Split . Однако это не позволит мне (легко) разбить на новую строку, так каков наилучший способ сделать это?

RCIX
источник
2
Почему бы и нет? Просто разделить на System.Environment.NewLine
aviraldg
16
Но вы должны обернуть его в строку [] и добавить дополнительный аргумент и ... это просто кажется неуклюжим.
RCIX

Ответы:

1414

Чтобы разбить строку, вам нужно использовать перегрузку, которая принимает массив строк:

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

Редактировать:
Если вы хотите обрабатывать различные типы разрывов строк в тексте, вы можете использовать возможность сопоставлять более чем одну строку. Это правильно разделит любой тип разрыва строки и сохранит пустые строки и интервалы в тексте:

string[] lines = theText.Split(
    new[] { "\r\n", "\r", "\n" },
    StringSplitOptions.None
);
Guffa
источник
3
@RCIX: отправлять правильные параметры в метод немного неудобно, потому что вы используете его для чего-то, что намного проще, чем то, на что он способен. По крайней мере, там, до фреймворка 2 вам приходилось использовать регулярное выражение или создавать собственную подпрограмму расщепления для разбиения на строку ...
Guffa
4
@Leandro: Environment.NewLineсвойство содержит новую строку по умолчанию для системы. Например, для системы Windows это будет "\r\n".
Guffa
3
@Leandro: Одним из предположений может быть то, что программа разделяется, \nоставляя \rточку в конце каждой строки, а затем выводит строки \r\nмежду ними.
Guffa
3
@Samuel: последовательности \rи \nescape (среди прочих) имеют особое значение для компилятора C #. VB не имеет этих escape-последовательностей, поэтому вместо них используются эти константы.
Гуффа
2
Если вы хотите принимать файлы из множества различных ОС, вы также можете добавить «\ n \ r» в начало и «\ r» в конец списка разделителей. Я не уверен, что это стоит того, чтобы ударить по производительности. ( en.wikipedia.org/wiki/Newline )
user420667
121

Как насчет использования StringReader?

using (System.IO.StringReader reader = new System.IO.StringReader(input)) {
    string line = reader.ReadLine();
}
Клеман
источник
13
Это моя любимая. Я завернул в метод расширения и вернул текущую строку возврата: gist.github.com/ronnieoverby/7916886
Ронни Оверби
3
Это единственное решение без регулярных выражений, которое я нашел для .netcf 3.5
Carl
8
Особенно приятно, когда ввод большой и копирование его в массив становится медленным / интенсивно использует память.
Алехандро
1
Как написано, этот ответ читает только первую строку. См . Ответ Стива Купера для whileцикла, который должен быть добавлен к этому ответу.
ToolmakerSteve
48

Вы должны быть в состоянии разбить вашу строку довольно легко, например так:

aString.Split(Environment.NewLine.ToCharArray());
nikmd23
источник
46
В системе, отличной от * nix, которая разделяется на отдельные символы в строке Newline, то есть символы CR и LF. Это приведет к дополнительной пустой строке между каждой строкой.
Гуффа
Поправьте меня, если я ошибаюсь, но разве это не разделит символы \ и n?
RCIX
7
@RCIX: Нет, коды \ r и \ n представляют отдельные символы. Строка "\ r \ n" состоит из двух символов, а не четырех.
Гуффа
10
если вы добавите параметр StringSplitOptions.RemoveEmptyEntries, то это будет работать отлично.
Рубен
18
@Ruben: Нет, не будет. Серж уже предложил это в своем ответе, а я уже объяснил, что он также удалит пустые строки в исходном тексте, которые следует сохранить.
Гуффа
34

Старайтесь избегать использования string.Split для общего решения, потому что вы будете использовать больше памяти везде, где вы используете функцию - исходную строку и разделенную копию, как в памяти. Поверьте мне, что это может быть одной из самых серьезных проблем, когда вы начинаете масштабирование - запустите 32-разрядное приложение пакетной обработки, обрабатывающее документы объемом 100 МБ, и вы справитесь с восемью одновременными потоками. Не то чтобы я был там раньше ...

Вместо этого используйте такой итератор;

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

Это позволит вам сделать более эффективный цикл памяти вокруг ваших данных;

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

Конечно, если вы хотите все это в памяти, вы можете сделать это;

var allTheLines = document.SplitToLines.ToArray();
Стив Купер
источник
Я был там ... (парсинг больших файлов HTML и нехватка памяти). Да, избегайте строк. Использование string.Split может привести к использованию кучи больших объектов (LOH) - но я не уверен на 100% в этом.
Питер Мортенсен
Если вы сделали SplitToLines статическим методом (который, кажется, вы dd), то как вы можете это сделать, blah.SplitToLines.. например document.SplitToLines...?
Бароп
Ах, я вижу, вы вводите thisформальные параметры, что делает его методом расширения.
Barlop
26

Основываясь на ответе Гуффы, в классе расширения используйте:

public static string[] Lines(this string source) {
    return source.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.None);
}
Эрвин Майер
источник
9

Для строковой переменной s:

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

При этом используется определение окончаний строк в вашей среде. В Windows окончание строки - CR-LF (возврат каретки, перевод строки) или escape-символы C #\r\n .

Это надежное решение, потому что если вы рекомбинируете строки с String.Join, это равно вашей исходной строке:

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

Что не делать:

  • использование StringSplitOptions.RemoveEmptyEntries , потому что это сломает разметку, такую ​​как Markdown, где пустые строки имеют синтаксическое назначение.
  • Разделить на разделитель new char[]{Environment.NewLine}, потому что в Windows это создаст один пустой строковый элемент для каждой новой строки.
Полковник паника
источник
В основном тот же ответ здесь, что и самый высокий, принятый, но у него есть хороший юнит-тест и предостережения.
vapcguy
8

Regex также вариант:

    private string[] SplitStringByLineFeed(string inpString)
    {
        string[] locResult = Regex.Split(inpString, "[\r\n]+");
        return locResult;
    }
user1964822
источник
7
Если вы хотите , чтобы соответствовать линии точно, сохраняя пустые строки, это регулярное выражение строка будет лучше: "\r?\n".
Рори О'Кейн
7

Я просто подумал, что добавлю свои два бита, потому что другие решения по этому вопросу не попадают в классификацию кода для повторного использования и не удобны.

Следующий блок кода расширяет stringобъект, чтобы он был доступен как естественный метод при работе со строками.

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

Теперь вы можете использовать .Split()функцию из любой строки следующим образом:

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

Чтобы разделить символ новой строки, просто передайте "\n"или "\r\n"в качестве параметра разделителя.

Комментарий: было бы неплохо, если бы Microsoft реализовала эту перегрузку.

Краанг Прайм
источник
Environment.NewlineПредпочтительно , чтобы жесткое кодирование либо \nили \r\n.
Майкл Блэкберн
3
@MichaelBlackburn - это неверное утверждение, потому что нет контекста. Environment.Newlineпредназначен для кроссплатформенной совместимости, а не для работы с файлами, использующими другие окончания строки, чем в текущей операционной системе. Смотрите здесь для получения дополнительной информации , так что это действительно зависит от того, с чем работает разработчик. Использование Environment.Newlineобеспечивает отсутствие согласованности в типе возврата строки между ОС, где «жесткое кодирование» дает разработчику полный контроль.
Краанг Прайм
2
@MichaelBlackburn - Вам не нужно быть грубым. Я просто предоставлял информацию. .Newlineне волшебство, под капотом это просто строки, как указано выше, основанные на переключателе, если он работает на Unix или Windows. Самая безопасная ставка - сначала заменить строку на все "\ r \ n", а затем разделить на "\ n". Когда использование .Newlineне удается, это когда вы работаете с файлами, которые сохранены другими программами, которые используют другой метод для разрывов строк. Это работает хорошо, если вы знаете, что каждый раз, когда файл читается, всегда использует разрывы строк вашей текущей ОС.
Краанг Прайм
То, что я слышу, - это самый читаемый способ (возможно, более интенсивное использование памяти) foo = foo.Replace("\r\n", "\n"); string[] result = foo.Split('\n');. Я правильно понимаю, что это работает на всех платформах?
Джон Доу
4

В настоящее время я использую эту функцию (на основе других ответов) в VB.NET:

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

Сначала он пытается разделить новую строку на платформе, а затем возвращается к каждой возможной новой строке.

Мне нужно было это только в одном классе. Если это изменится, я, вероятно, сделаю этоPublic и переместу в служебный класс, и, возможно, даже сделаю его методом расширения.

Вот, как объединить строки, для хорошей меры:

Private Shared Function JoinLines(lines As IEnumerable(Of String)) As String
    Return String.Join(Environment.NewLine, lines)
End Function
Рори О'Кейн
источник
@ Самуил - обратите внимание на цитаты. Они на самом деле имеют это значение. "\r"= возврат "\r\n"= возврат + новая строка. (пожалуйста, просмотрите этот пост и принятое решение здесь
Kraang Prime
@ Краанг Хмм .. Я давно не работал с .NET. Я был бы удивлен, если бы так много людей проголосовали за неправильный ответ. Я вижу, что тоже прокомментировал ответ Гуффы и получил там разъяснения. Я удалил свой комментарий к этому ответу. Спасибо за внимание.
Самуил
2

Ну, на самом деле сплит должен сделать:

//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
источник
2
Параметр RemoveEmptyEntries удалит пустые строки из текста. Это может быть желательно в некоторых ситуациях, но простой разделитель должен сохранять пустые строки.
Гуффа
да, вы правы, я просто сделал это предположение, что ... ну, пустые строки не интересны;)
MaciekTalaska
1
string[] lines = text.Split(
  Environment.NewLine.ToCharArray(), 
  StringSplitOptions.RemoveEmptyStrings);

В RemoveEmptyStrings гарантирует, что у вас нет пустых записей из-за \ n после \ r

(Изменить, чтобы отразить комментарии :) Обратите внимание, что в тексте также будут отбрасываться подлинные пустые строки. Обычно это то, что я хочу, но это не может быть вашим требованием.

Серж Вотье
источник
Параметры RemoveEmptyStrings также удаляют пустые строки, поэтому они не будут работать должным образом, если в тексте есть пустые строки.
Гуффа
Вы, вероятно, хотите сохранить настоящие пустые строки: \ r \ n \ r \ n
тонкий
0

Я не знал о Environment.Newline, но, думаю, это очень хорошее решение.

Моя попытка была бы:

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

Дополнительный .Trim удаляет все \ r или \ n, которые могут все еще присутствовать (например, в окнах, но разделение строки символами новой строки os x). Вероятно, не самый быстрый способ, хотя.

РЕДАКТИРОВАТЬ:

Как правильно отмечено в комментариях, это также удаляет все пробелы в начале строки или перед новой строкой. Если вам нужно сохранить этот пробел, используйте один из других вариантов.

Максимум
источник
Обрезка также удалит все пробелы в начале и конце строк, например отступ.
Гуффа
«.Trim удаляет все \ r или \ n, которые могут все еще присутствовать» - ой. Почему бы не написать надежный код вместо этого?
bzlm
Возможно, я неправильно понял вопрос, но было / не ясно, что пробел должен быть сохранен. Конечно, вы правы, Trim () также удаляет пробелы.
Макс
1
@Max: Ух ты, подожди, пока я скажу своему боссу, что коду разрешено делать все, что конкретно не исключено в спецификации ...;)
Guffa
-2

Глупый ответ: написать во временный файл, чтобы вы могли использовать почтенный 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
Избегайте var, поскольку он не определяет тип переменной, поэтому вы можете не понимать, как использовать этот объект или что этот объект представляет. Кроме того, это показывает написание строк и даже не указывает имя файла, поэтому я сомневаюсь, что это сработает. Тогда при чтении путь к файлу опять не указывается. Предполагая, что pathэто так C:\Temp\test.txt, вы должны иметь string[] lines = File.ReadLines(path);.
vapcguy
1
@vapcguy что я только что прочитал? - Я бы порекомендовал перечитать пост или отладить его в консольной программе, потому что все, что вы сказали, просто неправильно | путь установлен на Path.GetTempFileName | var является распространенным и рекомендуемым определением в C # - кстати, оно определяет тип переменной ...... РЕДАКТИРОВАТЬ: Я не говорю, что это хорошее решение
koanbock
@koanbock Хорошо, поэтому я посмотрел Path.GetTempFileName msdn.microsoft.com/en-us/library/… и там написано, что он создает файл с нулевым байтом и возвращает «полный путь к этому файлу». Я мог поклясться, что пытался сделать это раньше, и это дало исключение, потому что он не нашел файл, а вместо этого вернул расположение папки. Я знаю аргументы для использования var, но я бы сказал, что это НЕ рекомендуется, потому что он не показывает, что такое переменный объект. Это запутывает это.
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

Очень просто, на самом деле.

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
источник
4
Совершенно неверно и не работает. Плюс, в C # это так Environment.NewLineже, как в VB.
vapcguy
Видите идентификатор конца строки в VB.NET? для разных вариантов новой строки.
Питер Мортенсен