为什么正则表达式不关心字符串长度
Why regex does not care about string length
为什么正则表达式的输入长度不影响性能,这怎么可能?
生成的字符串是这样的:128个随机字符。然后是括号内的两个数字。这重复了很多次。
128 radnom characters....(-2435346|45436) 128 radnom characters....(-32525562|-325346)
正则表达式获取括号内的所有数字。这是模式。
\(([-+]?\d+\|[-+]?\d+)\)
所以比赛会像
-2435346|45436
-32525562|-325346
etc...
这是执行基准测试的代码。我在生成输入后启动秒表,因为我只想评估匹配时间。
Random rand = new Random();
Func<string> getRandString = // generates 128 random characters.
() => Enumerable.Range(0, 128).Select(x => (char) rand.Next()).Aggregate("", (a, b) => a + b);
Func<int> getRandInteger = () => rand.Next(); // generates random number.
string format = "{0}({1}|{2})";
// Generate the big string.
StringBuilder bigstr = new StringBuilder();
for (int i = 0; i < 100; i++) // repeat 100 times.
{
bigstr.Append(string.Format(format, getRandString(), getRandInteger(), getRandInteger()));
}
string input = bigstr.ToString();
Stopwatch stopwatch = Stopwatch.StartNew();
var matches = Regex.Matches(input, @"\(([-+]?\d+\|[-+]?\d+)\)");
stopwatch.Stop();
Console.WriteLine("Time Elapsed :\t{0}\nInputLength :\t{1}\nMatches Count :\t{2}", stopwatch.Elapsed, input.Length, matches.Count);
如果我重复循环 10
次,这是我的控制台中的输出。
Time Elapsed : 00:00:00.0004132
InputLength : 1500
Matches Count : 10
如果我重复循环 1000
次。
Time Elapsed : 00:00:00.0004373 // seriously?
InputLength : 149937
Matches Count : 1000
如果我重复循环 1000000
次。
Time Elapsed : 00:00:00.0004900 // wtf?
InputLength : 149964452
Matches Count : 1000000
不信截图
这是某种懒惰的评价吗?如果是这样那么它如何显示匹配数?我是如何在调试器下做到这一点的,我可以看到匹配项。
我的正则表达式模式中有什么特别的东西可以让它变快吗?但是字符串长度如何不影响性能呢?看不懂。
发生这种情况的原因之一是 matches.Count
属性 被 延迟计算 。当您停止秒表时,匹配器准备进行匹配,但它没有找到任何东西。只有当你调用 matches.Count
时它才开始工作,但你此时已经停止计时了。
一个简单的解决方案是将 matches.Count
调用移动到您计时的代码部分。
另一个原因是您将解析正则表达式所需的时间包括在内。这是一个相对昂贵的操作,所以你应该在打开秒表之前进行更好的计时:
Regex testRegex = new Regex(@"\(([-+]?\d+\|[-+]?\d+)\)");
Stopwatch stopwatch = Stopwatch.StartNew();
var matches = testRegex.Matches(input);
var matchesCount = matches.Count;
stopwatch.Stop();
Console.WriteLine("Time Elapsed :\t{0}\nInputLength :\t{1}\nMatches Count :\t{2}", stopwatch.Elapsed, input.Length, matchesCount);
现在 10 场比赛与 10,000 场比赛的时间增长变得相当可观 (00:00:00.0129844
vs. 00:00:00.0843982
),尽管输入长度相差千倍时没有预期的那么大。
为什么正则表达式的输入长度不影响性能,这怎么可能?
生成的字符串是这样的:128个随机字符。然后是括号内的两个数字。这重复了很多次。
128 radnom characters....(-2435346|45436) 128 radnom characters....(-32525562|-325346)
正则表达式获取括号内的所有数字。这是模式。
\(([-+]?\d+\|[-+]?\d+)\)
所以比赛会像
-2435346|45436
-32525562|-325346
etc...
这是执行基准测试的代码。我在生成输入后启动秒表,因为我只想评估匹配时间。
Random rand = new Random();
Func<string> getRandString = // generates 128 random characters.
() => Enumerable.Range(0, 128).Select(x => (char) rand.Next()).Aggregate("", (a, b) => a + b);
Func<int> getRandInteger = () => rand.Next(); // generates random number.
string format = "{0}({1}|{2})";
// Generate the big string.
StringBuilder bigstr = new StringBuilder();
for (int i = 0; i < 100; i++) // repeat 100 times.
{
bigstr.Append(string.Format(format, getRandString(), getRandInteger(), getRandInteger()));
}
string input = bigstr.ToString();
Stopwatch stopwatch = Stopwatch.StartNew();
var matches = Regex.Matches(input, @"\(([-+]?\d+\|[-+]?\d+)\)");
stopwatch.Stop();
Console.WriteLine("Time Elapsed :\t{0}\nInputLength :\t{1}\nMatches Count :\t{2}", stopwatch.Elapsed, input.Length, matches.Count);
如果我重复循环 10
次,这是我的控制台中的输出。
Time Elapsed : 00:00:00.0004132
InputLength : 1500
Matches Count : 10
如果我重复循环 1000
次。
Time Elapsed : 00:00:00.0004373 // seriously?
InputLength : 149937
Matches Count : 1000
如果我重复循环 1000000
次。
Time Elapsed : 00:00:00.0004900 // wtf?
InputLength : 149964452
Matches Count : 1000000
不信截图
这是某种懒惰的评价吗?如果是这样那么它如何显示匹配数?我是如何在调试器下做到这一点的,我可以看到匹配项。
我的正则表达式模式中有什么特别的东西可以让它变快吗?但是字符串长度如何不影响性能呢?看不懂。
发生这种情况的原因之一是 matches.Count
属性 被 延迟计算 。当您停止秒表时,匹配器准备进行匹配,但它没有找到任何东西。只有当你调用 matches.Count
时它才开始工作,但你此时已经停止计时了。
一个简单的解决方案是将 matches.Count
调用移动到您计时的代码部分。
另一个原因是您将解析正则表达式所需的时间包括在内。这是一个相对昂贵的操作,所以你应该在打开秒表之前进行更好的计时:
Regex testRegex = new Regex(@"\(([-+]?\d+\|[-+]?\d+)\)");
Stopwatch stopwatch = Stopwatch.StartNew();
var matches = testRegex.Matches(input);
var matchesCount = matches.Count;
stopwatch.Stop();
Console.WriteLine("Time Elapsed :\t{0}\nInputLength :\t{1}\nMatches Count :\t{2}", stopwatch.Elapsed, input.Length, matchesCount);
现在 10 场比赛与 10,000 场比赛的时间增长变得相当可观 (00:00:00.0129844
vs. 00:00:00.0843982
),尽管输入长度相差千倍时没有预期的那么大。