非常に簡単な解決策は、ユーザー名と実名から可能なすべての 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.
編集
e.* を削除して別のテストを行いましたが、余分な . そこに保管された
メソッド: RegexZeroWidthPlusOne かかった時間: 2601,1488ms。合格: 3333. 失敗 6666.
ご覧のとおり、.*
さらに 50% の遅延が発生しており、正規表現を使用して文字列を分割するすべてのソリューションは、string.Join を使用して 1 つの大きな式を作成するよりもはるかに遅くなります。明確な勝者は、正規表現を使用していないことです。
.*constant
よりも遅いという事実の説明は、constant
.* が最初に入力全体を取得し、次に定数を見つけるために (文字列の末尾から) バックトラックを開始しconstant
、最初のものを探すだけであるという事実による可能性があります。の発生constant
。
簡単なテストでこれを確認できるようです (.*?
の代わりに使用.*
):
メソッド: RegexZeroWidthPlusOneDotSplatReluctant 所要時間: 2646,1514ms。合格: 3333. 失敗 6666.
コードにいくつかの変更を加えました。大文字と小文字の区別チェックを削除しました (OP から要求されたものではありません)。引数の検証を削除しました。コードを早期に失敗するように変更しました。これにより、異なる方法間の公正な比較が保証されます。コードはここにあります。