The following solution keeps all whitespaces.
It first detects the kind (separator vs word/content) of any character and stores a list of chunks (where each item contains the start and end index, together with a boolean telling whether the chunk contains separators or word).
Then it writes the chunks to a result string in reversed order.
The order of characters inside each chunks is preserved, being the chunk a separator or word/content: this allow also to keep any double space or other chain of separators without need to post-check their sequence or quantity.
public static string Reverse(string text, Func<char, bool> separatorPredicate)
{
// Get all chars from source text
var aTextChars = text.ToCharArray();
// Find the start and end position of every chunk
var aChunks = new List<Tuple<int, int, bool>>();
{
var bLast = false;
var ixStart = 0;
// Loops all characters
for (int ixChar = 0; ixChar < aTextChars.Length; ixChar++)
{
var ch = aTextChars[ixChar];
// Current char is a separator?
var bNow = separatorPredicate(ch);
// Current char kind (separator/word) is different from previous
if ((ixChar > 0) && (bNow != bLast))
{
aChunks.Add(Tuple.Create(ixStart, ixChar - 1, bLast));
ixStart = ixChar;
bLast = bNow;
}
}
// Add remaining chars
aChunks.Add(Tuple.Create(ixStart, aTextChars.Length - 1, bLast));
}
var result = new StringBuilder();
// Loops all chunks in reverse order
for (int ixChunk = aChunks.Count - 1; ixChunk >= 0; ixChunk--)
{
var chunk = aChunks[ixChunk];
result.Append(text.Substring(chunk.Item1, chunk.Item2 - chunk.Item1 + 1));
}
return result.ToString();
}
public static string Reverse(string text, char[] separators)
{
return Reverse(text, ch => Array.IndexOf(separators, ch) >= 0);
}
public static string ReverseByPunctuation(string text)
{
return Reverse(text, new[] { ' ', '\t', '.', ',', ';', ':' });
}
public static string ReverseWords(string text)
{
return Reverse(text, ch => !char.IsLetterOrDigit(ch));
}
There are 4 methods:
- Reverse(string text, Func separatorPredicate) receives the source text and a delegate to determine when a character is a separator.
- Reverse(string text, char[] separators) receives the source text and an array of chars to be treated as separators (any other char is word/content).
- ReverseByPunctuation(string text) receives only the source text and delegates the computation to the first overload passing a predefined set of separator chars.
- ReverseWords(string text) receives only the source text and delegates the computation to the first overload passing a delegate that recognize as separator everything that is not a letter or digit.