0

实际上,我有一个小问题是我正在开发应用程序,其中注册用户的表单和验证结束密码不能包含用户名,并且不能包含连续的 2 个字母形式的用户名。

假设用户名 id 为“Aspnetmvc”,那么密码不应包含整个单词“Aspnetmvc”甚至用户名的一部分,如 asp、net、mvc。这可以通过自定义逻辑来解决,但我想做的是通过编程逻辑来解决,但我想做的是找到用正则表达式解决这个问题的方法。

有人知道这个 C# 吗?

4

2 回答 2

4

一个非常简单的解决方案是创建一个方法,该方法将从用户名和实名中提取所有可能的 3 个字母组合,并检查这些是否是密码的一部分。3 个字符的每个可能部分(超过 2 个)的方法可以编写为简单的扩展方法,然后您可以使用该IEnumerable.Any方法查看这些部分是否是密码的一部分:

using System;
using System.Linq;
using System.Collections.Generic;

namespace ConsoleApplication5
{
    static class Program
    {
        static void Main(string[] args)
        {
            string password = "1234567890";

            string username = "125689";
            string realName = "890";

            bool usernameOk = !username.AllPartsOfLength(3)
                .Any(part => password.Contains(part));
            bool realNameOk = !realName.AllPartsOfLength(3)
                .Any(part => password.Contains(part));
        }

        public static IEnumerable<string> AllPartsOfLength(this string value, int length)
        {
            for (int startPos = 0; startPos <= value.Length - length; startPos++)
            {
                yield return value.Substring(startPos, length);
            }
            yield break;
        }
    }
}

我发现这比任何包含正则表达式的解决方案更容易阅读。

你甚至可以这样做:

passwordOk = !username.AllPartsofLength(3)
    .Concat(realName.AllPartsOfLength(3))
    .Any(part => password.Contains(part));

由于这些使用惰性评估,因此在找到第一部分时评估将停止。

确实没有必要,也没有充分的理由尝试使用正则表达式来执行此操作。您可以使用的唯一表达式是检查字符串中是否存在任何 3 个字母部分的表达式。所以你仍然必须将字符串分成 3 部分,然后构建一个表达式,让运行时为此构建一个状态机,根据输入检查它,然后丢弃表达式。对于手头的问题,这是昂贵的。

它看起来像这样:

IEnumerable<string> parts = username.AllPartsOfLength(3)
    .Concat(realName.AllPartsOfLength(3))
    .Select(part => Regex.Escape(part));

string regex = "(" + string.Join("|", parts) + ")";

bool isPasswordOk = !Regex.Match(regex).Success;

添加基准

根据 sln 的要求,一个简短的基准:

方法:StringManipulationOnly 所用时间:26,0015 毫秒。通过:3333。失败 6666。

方法:RegexStringJoinAllParts 耗时:486,0278ms。通过:3333。失败 6666。

方法:RegexZeroWidthPlusOneAndDotSplat 耗时:5686,3252ms。通过:3333。失败 6666。

方法:RegexZeroWidth 耗时:2659,1521ms。通过:3333。失败 6666。

编辑 在删除 e.* 的情况下进行了另一次测试,但额外的 . 保存在那里

方法:RegexZeroWidthPlusOne 耗时:2601,1488ms。通过:3333。失败 6666。

正如您所看到的那样,要么.*导致另一个 50% 的延迟,而且所有使用正则表达式拆分字符串的解决方案都比使用 string.Join 创建一个大表达式慢得多。到目前为止,明显的赢家不是使用正则表达式。

.*constant对慢于的事实的解释constant可能是由于 .* 将首先获取整个输入,然后开始回溯(从字符串的末尾)以查找常量,而constant只会查找第一个的发生constant

一个简单的测试似乎证实了这一点(使用.*?代替.*):

方法:RegexZeroWidthPlusOneDotSplatReluctant 所用时间:2646,1514ms。通过:3333。失败 6666。

我确实对代码做了一些更改,我删除了区分大小写检查(OP 没有要求)我删除了参数验证,我将代码更改为提前失败。这确保了不同方法之间的公平比较。代码可以在这里找到

于 2012-04-29T16:25:54.823 回答
0

您应该让正则表达式为您完成工作(?=(..)).

重做 4-29

static class Program
{
    static void Main(string[] args)
    {
        string Password = "(O*@aJY^+{PC";
        string Account  = "email@Abc.com";
        string Name     = "Ted Nelson";
        if (Password.IsNotSequentialChars(Account, 2) && Password.IsNotSequentialChars(Name, 2))
            Console.WriteLine("Passed");
        else
            Console.WriteLine("Failed");
    }

    public static bool IsNotSequentialChars(this string Src, string Dest, int check_len)
    {
        if (check_len < 1 || Src.Length < check_len) return true;
        Match m = Regex.Match(Src, "(?=(.{" + check_len + "})).");
        bool bOK = m.Success;

        while (bOK && m.Success)
        {
            // Edit: remove unnecessary '.*' from regex.
            // And btw, is regex needed at all in this case?
            bOK = !Regex.Match(Dest, "(?i)" + Regex.Escape(m.Groups[1].Value)).Success;
            if (!bOK)
                Console.WriteLine("Destination contains " + check_len + " sequential source letter(s) '" + m.Groups[1].Value + "'");
            m = m.NextMatch();
        }
        return bOK;
    }
}

欢迎提供基准...

于 2012-04-27T22:08:28.680 回答