Here you go - this works any combination of delimiters in any order, allowing also for the situation where a delimiter is not actually found in the string either. It's taken me a while to come up with this and, having posted it, it looks more complex than any other answer!
Ah well, I'll keep it here anyway.
public static string SplitAndReJoin(string str, char[] delimiters,
Func<string[], string[]> mutator)
{
//first thing to know is which of the delimiters are
//actually in the string, and in what order
//Using ToArray() here to get the total count of found delimiters
var delimitersInOrder = (from ci in
(from c in delimiters
from i in FindIndexesOfAll(str, c)
select new { c, i })
orderby ci.i
select ci.c).ToArray();
if (delimitersInOrder.Length == 0)
return str;
//now split and mutate the string
string[] strings = str.Split(delimiters);
strings = mutator(strings);
//now build a format string
//note - this operation is much more complicated if you wish to use
//StringSplitOptions.RemoveEmptyEntries
string formatStr = string.Join("",
delimitersInOrder.Select((c, i) => string.Format("{{{0}}}", i)
+ c));
//deals with the 'perfect' split - i.e. there's always two values
//either side of a delimiter
if (strings.Length > delimitersInOrder.Length)
formatStr += string.Format("{{{0}}}", strings.Length - 1);
return string.Format(formatStr, strings);
}
public static IEnumerable<int> FindIndexesOfAll(string str, char c)
{
int startIndex = 0;
int lastIndex = -1;
while(true)
{
lastIndex = str.IndexOf(c, startIndex);
if (lastIndex != -1)
{
yield return lastIndex;
startIndex = lastIndex + 1;
}
else
yield break;
}
}
And here's a test you can use to validate it:
[TestMethod]
public void TestSplitAndReJoin()
{
//note - mutator does nothing
Assert.AreEqual("a,b", SplitAndReJoin("a,b", ",".ToCharArray(), s => s));
//insert a 'z' in front of every sub string.
Assert.AreEqual("zaaa,zbbbb.zccc|zdddd:zeee", SplitAndReJoin("aaa,bbbb.ccc|dddd:eee",
",.|:".ToCharArray(), s => s.Select(ss => "z" + ss).ToArray()));
//re-ordering of delimiters + mutate
Assert.AreEqual("zaaa,zbbbb.zccc|zdddd:zeee", SplitAndReJoin("aaa,bbbb.ccc|dddd:eee",
":|.,".ToCharArray(), s => s.Select(ss => "z" + ss).ToArray()));
//now how about leading or trailing results?
Assert.AreEqual("a,", SplitAndReJoin("a,", ",".ToCharArray(), s => s));
Assert.AreEqual(",b", SplitAndReJoin(",b", ",".ToCharArray(), s => s));
}
Note that I've assumed you need to be able to do something with the elements of the array, to manipulate the individual strings before joining them back together again - otherwise presumably you would just keep the original string!
The method builds a dynamic format string. No warranty here on efficiency :)