RegexクラスのIsMatch静的メソッドで、ある文字列が何らかのパターンにマッチするかを調べるための基本を取り上げる。
正規表現は、文字列を扱うプログラマーにとって(すなわち、ほとんどの開発者にとって)、必須のスキルである。まずはその基本となるパターンマッチングの方法を押さえておこう。本稿では、Regexクラス(System.Text.RegularExpressions名前空間)のIsMatchメソッドについて解説するとともに、よく使う簡単な正規表現の書き方なども紹介する。
なお、本稿で説明する正規表現は、.NET Frameworkのものである。JavaScriptの正規表現などでは細部が異なるので注意してほしい。
RegexクラスのIsMatch静的メソッドを使えばよい(次のコード)。
using System.Text.RegularExpressions;
……省略……
bool result = Regex.IsMatch("{検査対象文字列}", "{正規表現パターン}");
Imports System.Text.RegularExpressions
……省略……
Dim result As Boolean = Regex.IsMatch("{検査対象文字列}", "{正規表現パターン}")
ただし、IsMatch静的メソッドは、実行されるたびに引数の正規表現パターンを解析する。繰り返し同じマッチングを実行すると分かっている場合は、正規表現パターンをあらかじめコンパイルして保持するRegexオブジェクトを生成しておき、IsMatchインスタンスメソッドを使うと高速化できる(次のコード)。
using System.Text.RegularExpressions;
……省略……
var rx = new Regex("{正規表現パターン}", RegexOptions.Compiled);
bool result = rx.IsMatch("{検査対象文字列}" );
Imports System.Text.RegularExpressions
……省略……
Dim rx = New Regex("{正規表現パターン}", RegexOptions.Compiled)
Dim result As Boolean = rx.IsMatch("{検査対象文字列}")
パターンマッチングというと難しそうだが、正規表現パターンを使わなければStringクラス(System名前空間)のContainsメソッドと同じだ。
例えば、文字列中に「"吾輩は猫である"」という文字列が含まれているかどうかを検査するのに、Containsメソッドを使うなら次のコードのように書ける。
using static System.Console;
……省略……
WriteLine("吾輩は猫である。名前はまだ無い。".Contains("吾輩は猫である"));
// → True
WriteLine("吾輩は犬である。名前はまだ無い。".Contains("吾輩は猫である"));
// → False
Imports System.Console
……省略……
WriteLine("吾輩は猫である。名前はまだ無い。".Contains("吾輩は猫である"))
' → True
WriteLine("吾輩は犬である。名前はまだ無い。".Contains("吾輩は猫である"))
' → False
上と同じことを、RegexクラスのIsMatchメソッドを使うと、次のコードのように書ける。
using static System.Console;
using System.Text.RegularExpressions;
……省略……
WriteLine(Regex.IsMatch("吾輩は猫である。名前はまだ無い。", "吾輩は猫である"));
// → True
WriteLine(Regex.IsMatch("吾輩は犬である。名前はまだ無い。", "吾輩は猫である"));
// → False
Imports System.Console
Imports System.Text.RegularExpressions
……省略……
WriteLine(Regex.IsMatch("吾輩は猫である。名前はまだ無い。", "吾輩は猫である"))
' → True
WriteLine(Regex.IsMatch("吾輩は犬である。名前はまだ無い。", "吾輩は猫である"))
' → False
上のコードでは冒頭で2つの名前空間をインポートしている。同じインポートが以降のサンプルコードでも必要であるが、以降では省略させていただく。
ところで、猫だけでなく犬でもOKにしたい、すなわち、「"吾輩は猫である"」または「"吾輩は犬である"」という文字列が含まれているかどうかを検査したいときは、どうしたらよいだろうか? StringクラスのContainsメソッドを使うなら、2回検査して、それらの結果をORで結合するしかないだろう。
この「AまたはBが文字列に含まれているか?」という検査が、正規表現パターンを使うとIsMatchメソッド1回の呼び出しで済むのだ。
正規表現パターンの中で代替構成体「|」を使ってOR条件を指定する。OR条件の範囲を示すために「()」で囲む。「"吾輩は猫である"」または「"吾輩は犬である"」という文字列が含まれているかどうかを検査するには、「"吾輩は(猫|犬)である"」という正規表現パターンを使って次のコードのように書ける。
WriteLine(Regex.IsMatch("吾輩は猫である。名前はまだ無い。", "吾輩は(猫|犬)である"));
// → True
WriteLine(Regex.IsMatch("吾輩は犬である。名前はまだ無い。", "吾輩は(猫|犬)である"));
// → True
WriteLine(Regex.IsMatch("吾輩は鳥である。名前はまだ無い。", "吾輩は(猫|犬)である"));
// → False
WriteLine(Regex.IsMatch("吾輩は猫である。名前はまだ無い。", "吾輩は(猫|犬)である"))
' → True
WriteLine(Regex.IsMatch("吾輩は犬である。名前はまだ無い。", "吾輩は(猫|犬)である"))
' → True
WriteLine(Regex.IsMatch("吾輩は鳥である。名前はまだ無い。", "吾輩は(猫|犬)である"))
' → False
このように、StringクラスのContainsメソッドでは複数回の検査が必要だったマッチング処理が、正規表現を使うと1回で済むのだ。コーディング量が減るだけでなく、(正規表現が読めるならば)理解しやすいコードになるのである。
正規表現とは、複雑なパターンマッチング処理を簡潔に記述するためのものなのだ。そのためにさまざまな機能が正規表現に与えられているが、それが逆に正規表現を取っ付きにくいものにしているともいえる。まずは簡単な機能からちょっとずつ覚えていこう。
以降では、よく使う正規表現のパターンやオプションを紹介していく。
「^」は、パターンが文字列の先頭から始まることを表す。StringクラスのStartsWithメソッドに相当する。
「$」は、パターンが文字列の末尾で終わることを表す。StringクラスのEndsWithメソッドに相当する。
それぞれの例を次のコードに示す。
WriteLine(Regex.IsMatch("吾輩は猫である。名前はまだ無い。", "^名前はまだ無い。"));
// → False
// 次のStartsWithに相当
WriteLine("吾輩は猫である。名前はまだ無い。".StartsWith("名前はまだ無い。"));
// → False
WriteLine(Regex.IsMatch("吾輩は猫である。名前はまだ無い。", "名前はまだ無い。$"));
// → True
// 次のEndsWithに相当
WriteLine("吾輩は猫である。名前はまだ無い。".EndsWith("名前はまだ無い。"));
// → True
WriteLine(Regex.IsMatch("吾輩は猫である。名前はまだ無い。", "^名前はまだ無い。"))
' → False
' 次のStartsWithに相当
WriteLine("吾輩は猫である。名前はまだ無い。".StartsWith("名前はまだ無い。"))
' → False
WriteLine(Regex.IsMatch("吾輩は猫である。名前はまだ無い。", "名前はまだ無い。$"))
' → True
' 次のEndsWithに相当
WriteLine("吾輩は猫である。名前はまだ無い。".EndsWith("名前はまだ無い。"))
' → True
ここからは、Stringクラスの機能だけで実装するのは難しい処理になる(VBのLike演算子は、ある程度は似たことが可能)。
「.」は、ワイルドカードである。任意の1文字とマッチする(次のコード)。
WriteLine(Regex.IsMatch("吾輩は猫である。", "吾輩.猫")); // → True
WriteLine(Regex.IsMatch("吾輩も猫である。", "吾輩.猫")); // → True
WriteLine(Regex.IsMatch("吾輩もまた猫である。", "吾輩.猫")); // → False
WriteLine(Regex.IsMatch("吾輩は猫である。", "吾輩.猫")) ' → True
WriteLine(Regex.IsMatch("吾輩も猫である。", "吾輩.猫")) ' → True
WriteLine(Regex.IsMatch("吾輩もまた猫である。", "吾輩.猫")) ' → False
「[]」は、その中に書いた文字のどれか1文字にマッチする。いわば、制限付きのワイルドカードである。「[]」の中には、文字を範囲で指定してもよいし、文字を列挙してもよい(あるいは、両方を併用してもよい)。
また、「[]」の先頭に「^」を置くと、否定の意味になる(「[]」の中にない任意の1文字とマッチする)。
以上の例を次のコードに示す。
// 範囲指定
WriteLine(Regex.IsMatch("猫が5匹いる", "[1-9]匹")); // → True
WriteLine(Regex.IsMatch("猫が五匹いる", "[1-9]匹")); // → False
// 列挙
WriteLine(Regex.IsMatch("猫が5匹いる", "[123456789]匹")); // → True
// 範囲指定と列挙の併用
WriteLine(Regex.IsMatch("猫が5匹いる", "[1-46-9]匹")); // → False
// []内の先頭の「^」は否定の意味
WriteLine(Regex.IsMatch("猫が五匹いる", "[^1-9]匹")); // → True
// 全角文字でも範囲指定は可能
WriteLine(Regex.IsMatch("猫が5匹いる", "[1-9]匹")); // → True
// ただし漢数字には注意!
WriteLine(Regex.IsMatch("猫が五匹いる", "[一-九]匹")); // → False
' 範囲指定
WriteLine(Regex.IsMatch("猫が5匹いる", "[1-9]匹")) ' → True
WriteLine(Regex.IsMatch("猫が五匹いる", "[1-9]匹")) ' → False
' 列挙
WriteLine(Regex.IsMatch("猫が5匹いる", "[123456789]匹")) ' → True
' 範囲指定と列挙の併用
WriteLine(Regex.IsMatch("猫が5匹いる", "[1-46-9]匹")) ' → False
' []内の先頭の「^」は否定の意味
WriteLine(Regex.IsMatch("猫が五匹いる", "[^1-9]匹")) ' → True
' 全角文字でも範囲指定は可能
WriteLine(Regex.IsMatch("猫が5匹いる", "[1-9]匹")) ' → True
' ただし漢数字には注意!
WriteLine(Regex.IsMatch("猫が五匹いる", "[一-九]匹")) ' → False
文字または文字クラスの繰り返し回数を指定するものだ。文字クラスの繰り返しとは、同じ文字の繰り返しだけではない。例えば「[0-9]{3,}」と書くと、「数字が3文字以上続く」という意味になる。
以上の例を次のコードに示す。
// 量指定子「?」(0回または1回)
WriteLine(Regex.IsMatch("この月は寒い", "この[0-9]?月")); // → True
WriteLine(Regex.IsMatch("この1月は寒い", "この[0-9]?月")); // → True
WriteLine(Regex.IsMatch("この12月は寒い", "この[0-9]?月")); // → False
// 量指定子「*」(0回または1回以上)
WriteLine(Regex.IsMatch("この月は寒い", "この[0-9]*月")); // → True
WriteLine(Regex.IsMatch("この1月は寒い", "この[0-9]*月")); // → True
WriteLine(Regex.IsMatch("この12月は寒い", "この[0-9]*月")); // → True
// 量指定子「+」(1回以上)
WriteLine(Regex.IsMatch("この月は寒い", "この[0-9]+月")); // → False
WriteLine(Regex.IsMatch("この1月は寒い", "この[0-9]+月")); // → True
WriteLine(Regex.IsMatch("この12月は寒い", "この[0-9]+月")); // → True
// 量指定子「{n,m}」(n回以上、m回以下)
WriteLine(Regex.IsMatch("この月は寒い", "この[0-9]{2,2}月")); // → False
WriteLine(Regex.IsMatch("この1月は寒い", "この[0-9]{2,2}月")); // → False
WriteLine(Regex.IsMatch("この12月は寒い", "この[0-9]{2,2}月")); // → True
// 注:この例は、「{2,}」や「{2}」と書いても同じ
' 量指定子「?」(0回または1回)
WriteLine(Regex.IsMatch("この月は寒い", "この[0-9]?月")) ' → True
WriteLine(Regex.IsMatch("この1月は寒い", "この[0-9]?月")) ' → True
WriteLine(Regex.IsMatch("この12月は寒い", "この[0-9]?月")) ' → False
' 量指定子「*」(0回または1回以上)
WriteLine(Regex.IsMatch("この月は寒い", "この[0-9]*月")) ' → True
WriteLine(Regex.IsMatch("この1月は寒い", "この[0-9]*月")) ' → True
WriteLine(Regex.IsMatch("この12月は寒い", "この[0-9]*月")) ' → True
' 量指定子「+」(1回以上)
WriteLine(Regex.IsMatch("この月は寒い", "この[0-9]+月")) ' → False
WriteLine(Regex.IsMatch("この1月は寒い", "この[0-9]+月")) ' → True
WriteLine(Regex.IsMatch("この12月は寒い", "この[0-9]+月")) ' → True
' 量指定子「{n,m}」(n回以上、m回以下)
WriteLine(Regex.IsMatch("この月は寒い", "この[0-9]{2,2}月")) ' → False
WriteLine(Regex.IsMatch("この1月は寒い", "この[0-9]{2,2}月")) ' → False
WriteLine(Regex.IsMatch("この12月は寒い", "この[0-9]{2,2}月")) ' → True
' 注:この例は、「{2,}」や「{2}」と書いても同じ
マッチング処理の動作を変更するオプションがある。そのようなオプションは、IsMatchメソッドの引数として与えるか、正規表現の中に埋め込むインラインオプションとして与える。埋め込む場合は、正規表現の途中でオプション指定の変更が可能だ(解除するにはオプション文字の前に「-」を付ける)。複数のオプションを指定するには、引数の場合は論理OR演算子「|」(C#)/「Or」(VB)で結合し、埋め込む場合は単に並置する。引数か埋め込みかは自由だが、ただでさえ正規表現は複雑になりがちなので、(正規表現の途中で指定変更が必要な場合を除いて)引数で与えた方がよいだろう。
以下、主なものを3つ紹介する。
IgnoreCase/「(?i)」は、大文字/小文字の区別をしない(次のコード)。
// IsMatchの引数として与える
WriteLine(Regex.IsMatch("123abc", "[A-Z]+$"));
// → False
WriteLine(Regex.IsMatch("123abc", "[A-Z]+$", RegexOptions.IgnoreCase));
// → True
// 正規表現に埋め込む
WriteLine(Regex.IsMatch("123abc", "(?i)[A-Z]+$")); // → True
// 埋め込みオプションは「-」を付けると解除。正規表現の途中でON/OFFできる
WriteLine(Regex.IsMatch("123abc", "(?i)[A-Z](?-i)[A-Z]+$")); // → False
WriteLine(Regex.IsMatch("123aBC", "(?i)[A-Z](?-i)[A-Z]+$")); // → True
' IsMatchの引数として与える
WriteLine(Regex.IsMatch("123abc", "[A-Z]+$"))
' → False
WriteLine(Regex.IsMatch("123abc", "[A-Z]+$", RegexOptions.IgnoreCase))
' → True
' 正規表現に埋め込む
WriteLine(Regex.IsMatch("123abc", "(?i)[A-Z]+$")) ' → True
' 埋め込みオプションは「-」を付けると解除。正規表現の途中でON/OFFできる
WriteLine(Regex.IsMatch("123abc", "(?i)[A-Z](?-i)[A-Z]+$")) ' → False
WriteLine(Regex.IsMatch("123aBC", "(?i)[A-Z](?-i)[A-Z]+$")) ' → True
Multiline/「(?m)」は、「複数行モード」を指定する。対象の文字列が複数行から成っているとき、行ごとにマッチング処理をする(次のコード)。
var inputString1 = "123abc\n2行目";
WriteLine(Regex.IsMatch(inputString1, "[A-Z]+$",
RegexOptions.IgnoreCase)); // → False
WriteLine(Regex.IsMatch(inputString1, "[A-Z]+$",
RegexOptions.IgnoreCase | RegexOptions.Multiline)); // → True
WriteLine(Regex.IsMatch(inputString1, "(?im)[A-Z]+$")); // → True
// ただし、「\r\n」には対応していないので、実際には「\r?$」と書く
var inputString2
= @"123abc
2行目";
WriteLine(Regex.IsMatch(inputString2, "[a-z]+$",
RegexOptions.Multiline)); // → False
WriteLine(Regex.IsMatch(inputString2, "[a-z]+\r?$",
RegexOptions.Multiline)); // → True
// [\r\n]と書いてオプションなしでもよい(この方が分かりやすい)
WriteLine(Regex.IsMatch(inputString2, "[a-z]+[\r\n]")); // → True
Dim InputString1 = "123abc" + vbLf + "2行目"
WriteLine(Regex.IsMatch(InputString1, "[A-Z]+$", RegexOptions.IgnoreCase))
' → False
WriteLine(Regex.IsMatch(InputString1, "[A-Z]+$",
RegexOptions.IgnoreCase Or RegexOptions.Multiline))
' → True
WriteLine(Regex.IsMatch(InputString1, "(?im)[A-Z]+$"))
' → True
' ただし、vbCrLfには対応していないので、実際には「\r?$」と書く
Dim inputString2 = "123abc" + vbCrLf + "2行目"
WriteLine(Regex.IsMatch(inputString2, "[a-z]+$", RegexOptions.Multiline))
' → False
WriteLine(Regex.IsMatch(inputString2, "[a-z]+\r?$", RegexOptions.Multiline))
' → True
' [\r\n]と書いてオプションなしでもよい(この方が分かりやすい)
WriteLine(Regex.IsMatch(inputString2, "[a-z]+[\r\n]"))
' → True
Singleline/「(?s)」は、「単一行モード」を指定する。ワイルドカード「.」のマッチ対象は、既定では「\n」を除く文字なのだが、それが「\n」を含む全ての文字に変わる(次のコード)。
var inputString
= @"123abc
2行目";
WriteLine(Regex.IsMatch(inputString, "123.+2行目")); // → False
WriteLine(Regex.IsMatch(inputString, "123.+2行目",
RegexOptions.Singleline)); // → True
WriteLine(Regex.IsMatch(inputString, "(?s)123.+2行目")); // → True
Dim InputString = "123abc" + vbCrLf + "2行目"
WriteLine(Regex.IsMatch(InputString, "123.+2行目")) ' → False
WriteLine(Regex.IsMatch(InputString, "123.+2行目",
RegexOptions.Singleline)) ' → True
WriteLine(Regex.IsMatch(InputString, "(?s)123.+2行目")) ' → True
正規表現を使って文字列がパターンに一致するか調べるには、RegexクラスのIsMatch静的メソッドを使えばよい。繰り返し実行すると分かっている場合は、RegexOptions.Compiledオプションを付けてRegexインスタンスを作り、そのIsMatchインスタンスメソッドを使うとマッチング処理を高速にできる。
また、本稿では主な正規表現と実行時オプションも簡単に紹介した。詳しくはMSDNの「.NET Framework の正規表現」をご覧いただきたい。
利用可能バージョン:.NET Framework 1.1以降(サンプルコードにはそれ以降の構文も含む)
カテゴリ:クラス・ライブラリ 処理対象:文字列
使用ライブラリ:Regexクラス(System.Text.RegularExpressions名前空間)
使用ライブラリ:RegexOptions列挙体(System.Text.RegularExpressions名前空間)
関連TIPS:構文:クラス名を書かずに静的メソッドを呼び出すには?[C# 6.0]
関連TIPS:VB.NETでクラス名を省略してメソッドや定数を利用するには?
関連TIPS:正規表現を使って部分文字列を取得するには?[C#、VB]
関連TIPS:正規表現のパターン内にコメント文を記述するには?[C#、VB]
関連TIPS:正規表現を使って文字列から部分文字列を取り除くには?[C#、VB]
Copyright© Digital Advantage Corp. All Rights Reserved.