OK, how about we test it out?
Here's the test app:
namespace TestIt
{
using System;
using System.Diagnostics;
using System.Text.RegularExpressions;
class Program
{
static void Main()
{
string test;
int iterations = 10000;
Stopwatch stopWatch = new Stopwatch();
while ((test = Console.ReadLine()) != "")
{
int iterator = iterations;
stopWatch.Start();
while (iterator-- != 0) SplitWithStruct(test);
stopWatch.Stop();
Console.WriteLine(stopWatch.ElapsedTicks);
iterator = iterations;
stopWatch.Reset();
stopWatch.Start();
while (iterator-- != 0) SplitWithRegex(test);
stopWatch.Stop();
Console.WriteLine(stopWatch.ElapsedTicks);
stopWatch.Reset();
}
}
static Regex regex = new Regex(@"\D");
static string SplitWithRegex(string text)
{
return regex.Replace(text, "");
}
struct SplitString
{
public string Text;
public int Number;
}
static SplitString SplitWithStruct(string text)
{
int firstNumericChar = text.Length;
char[] chars = text.ToCharArray();
while (Char.IsNumber(chars[--firstNumericChar]) && (firstNumericChar > 0));
SplitString result;
result.Number = int.Parse(text.Substring(firstNumericChar + 1));
result.Text = text.Substring(0, firstNumericChar);
return result;
}
}
}
With minimal input (1 char and 1 digit) Regex is ~4.5 times faster. As input size doubles the Struct method run time nearly doubles, but Regex time increases at a much slower rate.
Why? There's two reasons. One is that regex is highly optimized. For the second reason let's look at the MSIL:
.method private hidebysig static string SplitWithRegex(string text) cil managed
{
// Code size 17 (0x11)
.maxstack 8
IL_0000: ldsfld class [system]System.Text.RegularExpressions.Regex ConsoleApplication1.Program::regex
IL_0005: ldarg.0
IL_0006: ldstr ""
IL_000b: callvirt instance string [system]System.Text.RegularExpressions.Regex::Replace(string,
string)
IL_0010: ret
} // end of method Program::SplitWithRegex
.method private hidebysig static valuetype ConsoleApplication1.Program/SplitString
SplitWithStruct(string text) cil managed
{
// Code size 70 (0x46)
.maxstack 4
.locals init (int32 V_0,
char[] V_1,
valuetype ConsoleApplication1.Program/SplitString V_2)
IL_0000: ldarg.0
IL_0001: callvirt instance int32 [mscorlib]System.String::get_Length()
IL_0006: stloc.0
IL_0007: ldarg.0
IL_0008: callvirt instance char[] [mscorlib]System.String::ToCharArray()
IL_000d: stloc.1
IL_000e: ldloc.1
IL_000f: ldloc.0
IL_0010: ldc.i4.1
IL_0011: sub
IL_0012: dup
IL_0013: stloc.0
IL_0014: ldelem.u2
IL_0015: call bool [mscorlib]System.Char::IsNumber(char)
IL_001a: brfalse.s IL_0020
IL_001c: ldloc.0
IL_001d: ldc.i4.0
IL_001e: bgt.s IL_000e
IL_0020: ldloca.s V_2
IL_0022: ldarg.0
IL_0023: ldloc.0
IL_0024: ldc.i4.1
IL_0025: add
IL_0026: callvirt instance string [mscorlib]System.String::Substring(int32)
IL_002b: call int32 [mscorlib]System.Int32::Parse(string)
IL_0030: stfld int32 ConsoleApplication1.Program/SplitString::Number
IL_0035: ldloca.s V_2
IL_0037: ldarg.0
IL_0038: ldc.i4.0
IL_0039: ldloc.0
IL_003a: callvirt instance string [mscorlib]System.String::Substring(int32,
int32)
IL_003f: stfld string ConsoleApplication1.Program/SplitString::Text
IL_0044: ldloc.2
IL_0045: ret
} // end of method Program::SplitWithStruct
See all those calvirts? They add overhead that Regex doesn't have.
So it appears that Regex is much much faster. Could you write a faster function? Maybe, but it would be very difficult and the gains would be minimal.
BTW, I'm not a fan of Regex myself. The Regex filter strings are nearly impossible to read by the uninitiated.