Как вы разбиваете многострочную строку на строки?
Я знаю этот путь
var result = input.Split("\n\r".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
выглядит немного уродливым и теряет пустые строки. Есть ли лучшее решение?
Как вы разбиваете многострочную строку на строки?
Я знаю этот путь
var result = input.Split("\n\r".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
выглядит немного уродливым и теряет пустые строки. Есть ли лучшее решение?
Если это выглядит уродливым, просто удалите ненужный вызов ToCharArray.
Если вы хотите разделить на \n или \r, у вас есть два варианта:
Используйте литерал массива - но это даст вам пустые строки для окончаний строк в стиле \r\n:
var result = text.Split(new [] { '\r', '\n' });
Используйте регулярное выражение, как указано Бартом:
var result = Regex.Split(text, "\r\n|\r|\n");
Если вы хотите сохранить пустые строки, почему вы явно говорите С#, чтобы выбросить их? (StringSplitOptions) - используйте StringSplitOptions.None.
using (StringReader sr = new StringReader(text)) {
    string line;
    while ((line = sr.ReadLine()) != null) {
        // do something
    }
}
		Вы можете использовать Regex.Split:
string[] tokens = Regex.Split(input, @"\r?\n|\r");
Изменить: добавлен |\r для учета (старых) терминаторов линии Mac.
Это прекрасно работает и работает быстрее, чем 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
		Если вы хотите сохранить пустые строки, просто удалите StringSplitOptions.
var result = input.Split(System.Environment.NewLine.ToCharArray());
		 У меня был этот другой ответ, но этот, основанный на ответе Джека, значительно быстрее может быть предпочтительным, поскольку он работает асинхронно, хотя и немного медленнее.
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();
		      char[] archDelim = new char[] { '\r', '\n' };
      words = asset.text.Split(archDelim, StringSplitOptions.RemoveEmptyEntries); 
		    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();
    }
		 Сложно правильно обрабатывать смешанные окончания строк. Как известно, символами завершения строки могут быть " \u000A строки" (ASCII 10, \n, \x0A, \u000A), "Возврат каретки" (ASCII 13, \r, \x0D, \u000D) или некоторая комбинация их. Возвращаясь к DOS, Windows использует двухсимвольную последовательность CR-LF \u000D\u000A, поэтому эта комбинация должна \u000D\u000A только одну строку. Unix использует один \u000A, а на очень старых компьютерах Mac используется один \u000D. Стандартный способ обработки произвольных смесей этих символов в одном текстовом файле заключается в следующем:
\u000D\u000A), то эти два вместе пропускают только одну строку.String.Empty - единственный вход, который не возвращает строк (любой символ влечет за собой хотя бы одну строку) Предыдущее правило описывает поведение StringReader.ReadLine и связанных функций, и функция, показанная ниже, дает идентичные результаты. Это эффективная функция разрыва строки С#, которая должным образом реализует эти рекомендации для правильной обработки любой произвольной последовательности или комбинации 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 при каждом вызове, вы можете использовать следующий код С# 7. Как уже отмечалось, хотя приведенный выше пример может быть несколько более эффективным, обе эти функции дают одинаковые результаты.
public static IEnumerable<String> Lines(this String s)
{
    using (var tr = new StringReader(s))
        while (tr.ReadLine() is String L)
            yield return L;
}