.NET Framework の C# のコードで、正規表現のパターンにコメントをつける方法を備忘録として書いておきます。
上の画像を見れば一目瞭然で、それ以上の説明は不要かもしれませんが、それではブログの記事としてはちょっと寂しいので追加情報なども以下に書いておきます。
正規表現については、自分的には MSDN の記事 ASP.NET の正規表現 が一番分かりやすく、いつも参考にさせてもらっています。
おかげさまで、自分でもある程度パターンを作れるようにはなったのですが、自分で作っておきながら後になって読むと意味不明ということがあります。
そういう時のために、正規表現パターンにコメントをつけておくと良いという MDSN の記事を見つけて真似しています。
その記事見つからなくなってしまったので、以前自分が書いたコードを探して、それを参考にしてコメントをつけていましたが、探して見つけるのが結構大変ということで、ブログに書いておくことにしました。
以下は完全に余談ですが・・・
例として、パスワードの文字列で、条件として「半角大文字アルファベットと半角数字のみの 4 文字以上、8 文字以下で構成され、それぞれ最低 1 文字を含む」というケースを考えてみます。
そのようなケースでは、上に紹介した「ASP.NET の正規表現」のページの「高度なトピック」のセクションに書いてある "ルックアラウンド処理" を利用するのが便利だと思います。
「正の先読み」および「負の先読み」の両方のケースで書いてみました。前者が下のコードの regex1、後者が regex2 です。
Regex regex1 = new Regex(@"
^ # 開始のアンカー
(?=.*\d) # 数字が最低 1 文字あること
(?=.*[A-Z]) # 英大文字が最低 1 文字あること
[A-Z0-9]{4,8} # 英大文字または数字が 4 ~ 8 文字
$ # 終了のアンカー",
RegexOptions.IgnorePatternWhitespace);
Regex regex2 = new Regex(@"
(?!^[0-9]*$) # 全部が数字ということはない
(?!^[A-Z]*$) # 全部が英大文字ということはない
^ # 開始のアンカー
([A-Z0-9]{4,8}) # 英大文字または数字が 4 ~ 8 文字
$ # 終了のアンカー",
RegexOptions.IgnorePatternWhitespace);
string[] testStrings = { "AB1", "AB12", "ABCDEF", "123456",
"ABCDEFG1", "1234567A", "ABCD12345",
"ABC123dE", "ABCあ123", "ABC%1234" };
foreach (string s in testStrings)
{
Console.WriteLine(s + " => " + regex1.IsMatch(s) + " (1)");
Console.WriteLine(s + " => " + regex2.IsMatch(s) + " (2)");
}
// 結果は:
// AB1 => False (1)
// AB1 => False (2)
// AB12 => True (1)
// AB12 => True (2)
// ABCDEF => False (1)
// ABCDEF => False (2)
// 123456 => False (1)
// 123456 => False (2)
// ABCDEFG1 => True (1)
// ABCDEFG1 => True (2)
// 1234567A => True (1)
// 1234567A => True (2)
// ABCD12345 => False (1)
// ABCD12345 => False (2)
// ABC123dE => False (1)
// ABC123dE => False (2)
// ABCあ123 => False (1)
// ABCあ123 => False (2)
// ABC%1234 => False (1)
// ABC%1234 => False (2)
「正の先読み」というのが (?=<pattern>) という形のもので、対象文字列を先読みしていって <pattern> の条件に合えば true になります。例えば、上のコードでいうと regex1 の (?=.*\d) が該当します。<pattern> は .*\d で、「任意の文字 0 回以上の繰り返しのあと数字がある」という条件になります。
「負の先読み」というのは (?!<pattern>) という形のもので、「正の先読み」の逆すなわち否定になり、<pattern> の条件に合わないものが true になります。上のコードの regex2 の (?!^[0-9]*$) の場合、<pattern> は ^[0-9]*$ で、「文字列の最初から最後まで全ての文字が数字ではない」という条件になります。
後者の方はホントにこれでいいのか自信がないですが(汗)、上のコードの中のコメントに書いたマッチするか否かの結果を見る限りでは、よさそうな感じです。(笑)