减少正则表达式中一年终止符的步数

时间:2017-10-17 22:01:44

标签: c# regex passwords

在发现字符串附近或结尾处的一年时,我有一个关于正则表达式步数的问题。

采用如下字符串:

Chocolatechipcookie2017!

找到最后一年并不是真正的问题,有很多选择。有些比其他人快。然而,所有这些都有相当高的步数。

举个例子:

\d{2,}\b.{0,1}$

步数:24

或者:

\d{2,}.{0,1}$

步数:23

或(不是我最喜欢的):

$(?<=\d\d.)

步数:31

或更精确:

[12]{0,1}\d{2,3}.{0,1}$

步数:43

到目前为止,最快的:

^.*\d{2,}.{0,1}$

步数:10

我想要完成的是尽可能减少步数。在简短的字符串中,例如我的例子,问题不是太严重,但我的密码管理员可以并且很乐意生成任意长度的密码,并且有些服务实际上支持最多甚至超过512个字符的密码长度。

另外,我确实对此有一定的学术兴趣。因此,即使我的密码管理器的真实场景无关紧要,我仍然希望听到一些关于此的想法。

提前致谢!

1 个答案:

答案 0 :(得分:1)

步骤

使用regex101计步器

^.*(\d{4})        17
^.*\K\d{4}        16
\d{4}             23

注意:上面的第二个选项不适用于C#以及许多其他正则表达式。

基准

基准 - RegexHero

注意:基准可能因系统而异。

^.*(\d{4})           ~915,000/s
^.*([0-9]{4})        ~1,030,000/s
\d{4}                ~693,000/s
[0-9]{4}             ~910,000/s

使用Wiktor

中提到的RegexOptions.RightToLeft
^.*(\d{4})           ~1,233,000/s
^.*([0-9]{4})        ~1,340,000/s
\d{4}                ~698,000/s
[0-9]{4}             ~909,000/s

显然,使用从右到左选项可以提高我们用例的性能。

下图显示了使用RightToLeft选项时基准测试的比较。前10个结果用于[0-9]{4}正则表达式,最后10个用于^.*([0-9]{4})正则表达式。

Benchmark results

基准 - 以下代码

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text.RegularExpressions;

namespace Test
{
    class Program
    {
        static void Main(string[] args)
        {
            Stopwatch stopwatch = new Stopwatch();

            List<string> regexes = new List<string> {
                @"[0-9]{4}",
                @"^.*([0-9]{4})"
            };

            foreach (string regex in regexes)
            {
                List<TimeSpan> times = new List<TimeSpan>();

                for (int i = 0; i < 100; i++)
                {
                    stopwatch.Start();

                    Regex myRegex = new Regex(regex, RegexOptions.RightToLeft);
                    string strTargetString = @"Chocolatechipcookie2017!";

                    for (int j = 0; j < 100000; j++)
                    {
                        foreach (Match myMatch in myRegex.Matches(strTargetString))
                        {
                        }
                    }

                    stopwatch.Stop();
                    times.Add(stopwatch.Elapsed);
                }
                TimeSpan average = TimeSpan.FromMilliseconds(times.Average(t => t.TotalMilliseconds));
                Console.WriteLine($"Elapsed={average}");
            }

            Console.Read();
        }
    }
}

以上代码启用了RightToLeft选项

  • [0-9]{4}:3.710秒
  • ^.*([0-9]{4}):10.276秒

以上代码没有RightToLeft

  • [0-9]{4}:3.523秒
  • ^.*([0-9]{4}):10.956秒

答案

根据RegexHero获得的步数计数器和基准测试结果,效果最佳的正则表达式为^.*([0-9]{4})且启用了RightToLeft选项。

根据使用上述代码的基准测试结果,效果最佳的正则表达式为[0-9]{4}而未启用RightToLeft选项。

注意:这些测试只是测试正则表达式执行的速度,而不考虑初始正则表达式执行后的数据操作的性能。