密码不应包含用户的帐户名称或超过两个连续字符的用户全名部分

时间:2012-04-27 18:14:39

标签: c# regex

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

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

任何人都对这个C#有所了解吗?

2 个答案:

答案 0 :(得分: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,0015ms。通过:3333。失败6666。

     

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

     

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

     

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

修改 是否删除了另一个测试,但是额外的。留在那里

  

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

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

.*constantconstant慢的事实的解释可能是因为。*将首先获取整个输入,然后开始回溯(从字符串的末尾开始) )找到常量,而constant只会查找constant的第一次出现。

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

  

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

我确实对代码做了一些更改,我删除了区分大小写检查(OP未请求)我删除了参数验证,我将代码更改为提前失败。这确保了不同方法之间的公平比较。 The code can be found here

答案 1 :(得分: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;
    }
}

欢迎基准......