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


806

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


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

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

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

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

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

4
Совершенно неверно и не работает. Плюс, в C # это так Environment.NewLineже, как в VB.
— vapcguy

Видите идентификатор конца строки в VB.NET? для разных вариантов новой строки.
— Питер Мортенсен
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.