Если это выглядит некрасиво, просто удалите ненужный ToCharArrayвызов.
Если вы хотите разделить на \nили \r, у вас есть два варианта:
Используйте литерал массива - но это даст вам пустые строки для окончаний строк в стиле Windows \r\n:
var result = text.Split(new [] { '\r', '\n' });
Используйте регулярное выражение, как указано Барт:
var result = Regex.Split(text, "\r\n|\r|\n");
Если вы хотите сохранить пустые строки, почему вы явно указываете C # выбросить их? ( StringSplitOptionsпараметр) - использовать StringSplitOptions.Noneвместо.
Удаление ToCharArray сделает код зависимым от платформы (NewLine может быть '\ n')
Константин Спирин
1
@Will: на случай, если вы имели в виду меня, а не Константина: я считаю ( твердо ), что код синтаксического анализа должен стремиться работать на всех платформах (т.е. он также должен читать текстовые файлы, которые были закодированы на разных платформах, а не на исполняющей платформе ). Так что для синтаксического анализа Environment.NewLineэто недопустимо, насколько я понимаю. Фактически, из всех возможных решений я предпочитаю использовать регулярные выражения, поскольку только оно правильно обрабатывает все исходные платформы.
Конрад Рудольф
2
@Hamish Ну просто посмотрите документацию перечисления или посмотрите исходный вопрос! Это StringSplitOptions.RemoveEmptyEntries.
Конрад Рудольф
8
Как насчет текста, содержащего '\ r \ n \ r \ n'. string.Split вернет 4 пустые строки, однако с '\ r \ n' он должен дать 2. Будет хуже, если '\ r \ n' и '\ r' смешаны в одном файле.
имя пользователя
1
@SurikovPavel Используйте регулярное выражение. Это определенно предпочтительный вариант, поскольку он правильно работает с любой комбинацией окончаний строк.
Конрад Рудольф
136
using (StringReader sr = new StringReader(text)) {
string line;
while ((line = sr.ReadLine()) != null) {
// do something
}
}
Важно иметь "\r\n"первую строку в массиве, чтобы она воспринималась как разрыв одной строки. Вышеупомянутое дает те же результаты, что и любое из этих решений Regex:
Пожалуйста, добавьте дополнительные сведения, чтобы ваш ответ был более полезным для читателей.
Mohit Jain
Выполнено. Также добавлен тест для сравнения его производительности с решением Regex.
orad 08
Несколько более быстрый шаблон из-за меньшего количества откатов с той же функциональностью при использовании[\r\n]{1,2}
ΩmegaMan
@OmegaMan У него другое поведение. Он будет соответствовать \n\rили \n\nкак одинарный разрыв строки, что неверно.
orad
3
@OmegaMan Как обстоят дела с крайним Hello\n\nworld\n\nслучаем? Очевидно, что это одна строка с текстом, за которой следует пустая строка, за которой следует еще одна строка с текстом, за которой следует пустая строка.
Это не будет работать с текстовыми файлами в стиле OS X, поскольку они используются только в \rкачестве окончания строки.
Конрад Рудольф
2
@Konrad Rudolph: AFAIK, '\ r' использовалось в очень старых системах MacOS и почти никогда больше не встречается. Но если OP должен это учитывать (или, если я ошибаюсь), то регулярное выражение, конечно же, можно легко расширить для его учета: \ r? \ N | \ r
Барт Кирс
@Bart: Я не думаю , что вы ошибаешься , но я уже неоднократно сталкивался все возможные концовки линии в моей карьере программиста.
Конрад Рудольф
@ Конрад, наверное, ты прав. Думаю, лучше перестраховаться, чем сожалеть.
Барт Кирс
1
@ ΩmegaMan: при этом будут потеряны пустые строки, например \ n \ n.
Майк
10
Если вы хотите сохранить пустые строки, просто удалите StringSplitOptions.
var result = input.Split(System.Environment.NewLine.ToCharArray());
У меня был другой ответ, но этот, основанный на ответе Джека , значительно быстрее, может быть предпочтительнее, поскольку он работает асинхронно, хотя и немного медленнее.
publicstaticclassStringExtensionMethods
{
publicstatic IEnumerable<string> GetLines(thisstring str, bool removeEmptyLines = false)
{
using (var sr = new StringReader(str))
{
string line;
while ((line = sr.ReadLine()) != null)
{
if (removeEmptyLines && String.IsNullOrWhiteSpace(line))
{
continue;
}
yieldreturn line;
}
}
}
}
Мне интересно, не потому ли, что вы фактически не проверяете результаты перечислителя, и, следовательно, он не выполняется. К сожалению, мне лень проверять.
Джеймс
Да, это действительно так !! Когда вы добавляете .ToList () к обоим вызовам, решение StringReader действительно работает медленнее! На моей машине это 6,74 с против 5,10 с
JCH2k 02
Это имеет смысл. Я по-прежнему предпочитаю этот метод, потому что он позволяет мне получать строки асинхронно.
orad
Может быть, вам следует удалить заголовок «Лучшее решение» в другом ответе и отредактировать этот ...
Сложно правильно обрабатывать смешанные окончания строк. Как мы знаем, символы завершения строки может быть « перевод строки» (ASCII 10, \n, \x0A, \u000A), «Возврат каретки» (ASCII 13, \r, \x0D, \u000D), или какой - либо их сочетание. Возвращаясь к DOS, Windows использует двухсимвольную последовательность CR-LF \u000D\u000A, поэтому эта комбинация должна выдавать только одну строку. Unix использует \u000Aединственный \u000Dсимвол , а очень старые Mac использовали единственный символ. Стандартный способ обработки произвольных сочетаний этих символов в одном текстовом файле следующий:
каждый символ CR или LF должен переходить к следующей строке, КРОМЕ ...
... если за CR сразу следует LF ( \u000D\u000A), то эти два вместе пропускают только одну строку.
String.Empty - единственный ввод, который не возвращает строк (любой символ влечет за собой хотя бы одну строку)
Последняя строка должна быть возвращена, даже если в ней нет ни CR, ни LF.
Предыдущее правило описывает поведение StringReader.ReadLine и связанных функций, а функция, показанная ниже, дает идентичные результаты. Это эффективная функция разрыва строки C #, которая должным образом реализует эти рекомендации для правильной обработки любой произвольной последовательности или комбинации CR / LF. Пронумерованные строки не содержат символов CR / LF. Пустые строки сохраняются и возвращаются как String.Empty.
///<summary>/// Enumerates the text lines from the string./// ⁃ Mixed CR-LF scenarios are handled correctly/// ⁃ String.Empty is returned for each empty line/// ⁃ No returned string ever contains CR or LF///</summary>publicstatic IEnumerable<String> Lines(this String s)
{
int j = 0, c, i;
char ch;
if ((c = s.Length) > 0)
do
{
for (i = j; (ch = s[j]) != '\r' && ch != '\n' && ++j < c;)
;
yieldreturn s.Substring(i, j - i);
}
while (++j < c && (ch != '\r' || s[j] != '\n' || ++j < c));
}
Примечание. Если вы не возражаете против накладных расходов на создание StringReaderэкземпляра при каждом вызове, вместо этого можно использовать следующий код C # 7 . Как уже отмечалось, хотя приведенный выше пример может быть немного более эффективным, обе эти функции дают одинаковые результаты.
publicstatic IEnumerable<String> Lines(this String s)
{
using (var tr = new StringReader(s))
while (tr.ReadLine() is String L)
yieldreturn L;
}
Ответы:
Если это выглядит некрасиво, просто удалите ненужный
ToCharArray
вызов.Если вы хотите разделить на
\n
или\r
, у вас есть два варианта:Используйте литерал массива - но это даст вам пустые строки для окончаний строк в стиле Windows
\r\n
:var result = text.Split(new [] { '\r', '\n' });
Используйте регулярное выражение, как указано Барт:
var result = Regex.Split(text, "\r\n|\r|\n");
Если вы хотите сохранить пустые строки, почему вы явно указываете C # выбросить их? (
StringSplitOptions
параметр) - использоватьStringSplitOptions.None
вместо.источник
Environment.NewLine
это недопустимо, насколько я понимаю. Фактически, из всех возможных решений я предпочитаю использовать регулярные выражения, поскольку только оно правильно обрабатывает все исходные платформы.StringSplitOptions.RemoveEmptyEntries
.using (StringReader sr = new StringReader(text)) { string line; while ((line = sr.ReadLine()) != null) { // do something } }
источник
string.Split
илиRegex.Split
)?Обновление: см. Здесь альтернативное / асинхронное решение.
Это отлично работает и быстрее, чем Regex:
input.Split(new[] {"\r\n", "\r", "\n"}, StringSplitOptions.None)
Важно иметь
"\r\n"
первую строку в массиве, чтобы она воспринималась как разрыв одной строки. Вышеупомянутое дает те же результаты, что и любое из этих решений Regex:Regex.Split(input, "\r\n|\r|\n") Regex.Split(input, "\r?\n|\r")
За исключением того, что Regex оказывается примерно в 10 раз медленнее. Вот мой тест:
Action<Action> measure = (Action func) => { var start = DateTime.Now; for (int i = 0; i < 100000; i++) { func(); } var duration = DateTime.Now - start; Console.WriteLine(duration); }; var input = ""; for (int i = 0; i < 100; i++) { input += "1 \r2\r\n3\n4\n\r5 \r\n\r\n 6\r7\r 8\r\n"; } measure(() => input.Split(new[] {"\r\n", "\r", "\n"}, StringSplitOptions.None) ); measure(() => Regex.Split(input, "\r\n|\r|\n") ); measure(() => Regex.Split(input, "\r?\n|\r") );
Выход:
00: 00: 03.8527616
00: 00: 31.8017726
00: 00: 32.5557128
и вот метод расширения:
public static class StringExtensionMethods { public static IEnumerable<string> GetLines(this string str, bool removeEmptyLines = false) { return str.Split(new[] { "\r\n", "\r", "\n" }, removeEmptyLines ? StringSplitOptions.RemoveEmptyEntries : StringSplitOptions.None); } }
Применение:
input.GetLines() // keeps empty lines input.GetLines(true) // removes empty lines
источник
[\r\n]{1,2}
\n\r
или\n\n
как одинарный разрыв строки, что неверно.Hello\n\nworld\n\n
случаем? Очевидно, что это одна строка с текстом, за которой следует пустая строка, за которой следует еще одна строка с текстом, за которой следует пустая строка.Вы можете использовать Regex.Split:
string[] tokens = Regex.Split(input, @"\r?\n|\r");
Изменить: добавлено
|\r
в учетную запись для (старых) терминаторов строк Mac.источник
\r
качестве окончания строки.Если вы хотите сохранить пустые строки, просто удалите StringSplitOptions.
var result = input.Split(System.Environment.NewLine.ToCharArray());
источник
string[] lines = input.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
источник
У меня был другой ответ, но этот, основанный на ответе Джека ,
значительно быстрее,может быть предпочтительнее, поскольку он работает асинхронно, хотя и немного медленнее.public static class StringExtensionMethods { public static IEnumerable<string> GetLines(this string str, bool removeEmptyLines = false) { using (var sr = new StringReader(str)) { string line; while ((line = sr.ReadLine()) != null) { if (removeEmptyLines && String.IsNullOrWhiteSpace(line)) { continue; } yield return line; } } } }
Применение:
input.GetLines() // keeps empty lines input.GetLines(true) // removes empty lines
Контрольная работа:
Action<Action> measure = (Action func) => { var start = DateTime.Now; for (int i = 0; i < 100000; i++) { func(); } var duration = DateTime.Now - start; Console.WriteLine(duration); }; var input = ""; for (int i = 0; i < 100; i++) { input += "1 \r2\r\n3\n4\n\r5 \r\n\r\n 6\r7\r 8\r\n"; } measure(() => input.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None) ); measure(() => input.GetLines() ); measure(() => input.GetLines().ToList() );
Выход:
00: 00: 03.9603894
00: 00: 00.0029996
00: 00: 04.8221971
источник
Немного закручено, но блок итератора для этого:
public static IEnumerable<string> Lines(this string Text) { int cIndex = 0; int nIndex; while ((nIndex = Text.IndexOf(Environment.NewLine, cIndex + 1)) != -1) { int sIndex = (cIndex == 0 ? 0 : cIndex + 1); yield return Text.Substring(sIndex, nIndex - sIndex); cIndex = nIndex; } yield return Text.Substring(cIndex + 1); }
Затем вы можете позвонить:
var result = input.Lines().ToArray();
источник
private string[] GetLines(string text) { List<string> lines = new List<string>(); using (MemoryStream ms = new MemoryStream()) { StreamWriter sw = new StreamWriter(ms); sw.Write(text); sw.Flush(); ms.Position = 0; string line; using (StreamReader sr = new StreamReader(ms)) { while ((line = sr.ReadLine()) != null) { lines.Add(line); } } sw.Close(); } return lines.ToArray(); }
источник
Сложно правильно обрабатывать смешанные окончания строк. Как мы знаем, символы завершения строки может быть « перевод строки» (ASCII 10,
\n
,\x0A
,\u000A
), «Возврат каретки» (ASCII 13,\r
,\x0D
,\u000D
), или какой - либо их сочетание. Возвращаясь к DOS, Windows использует двухсимвольную последовательность CR-LF\u000D\u000A
, поэтому эта комбинация должна выдавать только одну строку. Unix использует\u000A
единственный\u000D
символ , а очень старые Mac использовали единственный символ. Стандартный способ обработки произвольных сочетаний этих символов в одном текстовом файле следующий:\u000D\u000A
), то эти два вместе пропускают только одну строку.String.Empty
- единственный ввод, который не возвращает строк (любой символ влечет за собой хотя бы одну строку)Предыдущее правило описывает поведение StringReader.ReadLine и связанных функций, а функция, показанная ниже, дает идентичные результаты. Это эффективная функция разрыва строки C #, которая должным образом реализует эти рекомендации для правильной обработки любой произвольной последовательности или комбинации CR / LF. Пронумерованные строки не содержат символов CR / LF. Пустые строки сохраняются и возвращаются как
String.Empty
./// <summary> /// Enumerates the text lines from the string. /// ⁃ Mixed CR-LF scenarios are handled correctly /// ⁃ String.Empty is returned for each empty line /// ⁃ No returned string ever contains CR or LF /// </summary> public static IEnumerable<String> Lines(this String s) { int j = 0, c, i; char ch; if ((c = s.Length) > 0) do { for (i = j; (ch = s[j]) != '\r' && ch != '\n' && ++j < c;) ; yield return s.Substring(i, j - i); } while (++j < c && (ch != '\r' || s[j] != '\n' || ++j < c)); }
Примечание. Если вы не возражаете против накладных расходов на создание
StringReader
экземпляра при каждом вызове, вместо этого можно использовать следующий код C # 7 . Как уже отмечалось, хотя приведенный выше пример может быть немного более эффективным, обе эти функции дают одинаковые результаты.public static IEnumerable<String> Lines(this String s) { using (var tr = new StringReader(s)) while (tr.ReadLine() is String L) yield return L; }
источник