Разбить строку набором символов разделителей, включая эти символы в итоговый массив как элементы

Необходимо разбить строку определённым массивом символов разделителей char[], так чтобы эти символы тоже сохранились в итоговом массиве строк string[], как отдельные элементы. Разумеется, сохраняя исходный порядок.

Получилось реализовать только так:

 public static class StringExtension
 {
     public static string[] SplitAndSaveSeparators(this string str, char[] separators)
     {
         string temp = str;
         foreach (char chr in str)
             if (separators.Contains(chr))
                 temp = temp.Replace($"{chr}", $"{'~'}{chr}{'~'}");
         return temp.Split('~');
     }
 }

Очевидно, что это не самая лучшая реализация. Возможно получится реализовать с помощью регулярных выражений или же еще как-либо?


Ответы (2 шт):

Автор решения: Alexander Petrov

Регулярками можно сделать так.

Заключаем разделители в круглые и квадратные скобки. Например, разделители , и ;. Тогда итоговое выражение должно выглядеть так: ([,;]).
Квадратные скобки означают набор символов, а круглые необходимы для использования алгоритма работы этого метода.
Цитата из документации:

Если в выражении Regex.Split используются скобки, любой захваченный текст включается в результирующий массив строк.

Не забываем об экранировании специальных символов: \, [ - для этого пропускаем разделители через Regex.Escape. Если таковых гарантированно не будет, то можно это пропустить.

using System.Text.RegularExpressions;

string str = "ab,cd,;ef";
char[] separators = [',', ';'];

string escaped = Regex.Escape(new string(separators));
string pattern = $"([{escaped}])";

var result = Regex.Split(str, pattern)
    .Where(x => x.Length > 0);

foreach (var x in result)
    Console.WriteLine(x);

Если в исходной строке str могут идти несколько разделителей подряд: ,;, то после расщепления в результате будут пустые строки. Удаляем их с помощью Where. Можно его убрать, если такого гарантированно не случится.

Кстати, в вашей функции в этом случае следует добавить параметр StringSplitOptions.RemoveEmptyEntries в вызов Split.

→ Ссылка
Автор решения: rotabor
using System;
using System.Collections.Generic;
static class Program {
    static List<string> GetDelimited(string researched, char[] sought) {
        int i = 0, j;
        var res = new List<string>();
        while ((j = researched.IndexOfAny(sought, i)) > -1) {
            if (j > i) res.Add(researched.Substring(i, j - i));
            res.Add(researched.Substring(j, 1));
            i = j + 1;
        }
        if (i < researched.Length) res.Add(researched.Substring(i, researched.Length - i));
        return res;
    }
    static string GetFromConsole() { Console.Write("> "); return Console.ReadLine(); }
    static void Main() {
        var sc = new char[] { ',', ';' };
        string instr;
        while ((instr = GetFromConsole()) != "\t") // TAB to end
            foreach (var s in GetDelimited(instr, sc)) Console.WriteLine(s);
    }
}
→ Ссылка