实际上,我有一个小问题是我正在开发app,其中一个表单注册用户和验证结束密码不得包含用户名,并且不得包含连续的2个字母组成用户名。
假设用户名为“Aspnetmvc” 那么密码不应该包含整个单词“Aspnetmvc”,甚至不能包含asp,net,mvc等用户名的一部分。 这可以通过自定义逻辑来解决,但我想要做的是通过编程逻辑来解决,但我想要做的是找出用正则表达式解决这个问题的方法。
任何人都对这个C#有所了解吗?
答案 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创建一个大表达式慢得多。到目前为止,明显的赢家并没有使用正则表达式。
.*constant
比constant
慢的事实的解释可能是因为。*将首先获取整个输入,然后开始回溯(从字符串的末尾开始) )找到常量,而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;
}
}
欢迎基准......