|
|
先読みと後読み |
H.Kamifuji . |
先読みも後読みもパターンとして一致させたい文字列を記述しますが、実際には位置にマッチするものです。例えば行頭や行末にマッチするメタ文字がありましたが、先読みは記述したパターンに一致した文字列の先頭の位置にマッチします。少し分かりにくいですが、考え方だけでも確認して下さい。 当ページでは、Linux CentOS7 の Gnome で動作テストしています。 現在(2021/08)では、JDK-16.0.2 にアップされています。一部、上位互換について、見直しを行っていきます。 現在(2021/11)では、JDK-17.0.1 にアップされています。一部、上位互換について、見直しを行っていきます。 現在(2023/04)では、JDK-20.0.1 にアップされています。一部、上位互換について、見直しを行っていきます。 現在(2024/10)では、JDK-23 にアップされています。一部、上位互換について、見直しを行っていきます。 |
|
先読みを行うパターンは"(?="から")"の間に記述します。 "パターン(?=先読みパターン)" "(?=先読みパターン)パターン"先読みのパターンは、指定されたパターンがターゲット文字列に存在するかどうかを確認し、見つかった場合は見つかった文字列の先頭の位置にマッチします。つまり先読みのパターンは位置にマッチするパターンです。次の例を見てください。 ターゲット文字列 "ColorBlack is #000000" パターン "Color(?=Black)B"この場合、まずパターンの中の"Color"がターゲット文字列の中の"Color"にマッチし、残るターゲット文字列は"Black is #000000"となります。 Color Black is #000000 ----- ---------------- Color続いてパターンの中の"(?=Black)"がマッチするかどうか試します。残っているターゲット文字列は"Black is #000000"なのでマッチします。ここで通常のパターンであれば残るターゲット文字列は" is #000000"となるのですが先読みのパターンの場合は位置にマッチするだけなので、残っているターゲット文字列は"Black is #000000"のままです。 Color Black is #000000 ----- ----- ----------- Color Black最後にパターンの中の"B"がマッチするかどうか試します。残っているターゲット文字列は"Black is #000000"なのでマッチします。 Color B lack is #000000 ----- - --------------- Color Bつまり"Color(?=Black)B"のパターンは、"Color"の後に"B"が続く文字列にマッチするが、条件として"B"の後には"Black"と続かなけばならないということです。 この時パターン全体にマッチした文字列は"ColorB"となります。先読みパターンの部分はあくまで位置にマッチしているだけのため、マッチした部分文字列には含まれてきません。 もしターゲット文字列が"ColorBlue is #0000FF"だったら、先読みパターンにマッチしないためパターン全体もマッチしません。またパターン文字列が"ColorB"だったらターゲット文字列が"ColorBlack is #000000"であっても"ColorBlue is #0000FF"であってもマッチします。 なお"Color(?=Black)B"は"(?=ColorBlack)ColorB"と記述しても同じことです。 では実際に試してみます。 サンプルプログラム下記のサンプルを実行してみよう。/** * 先読みを使ったパターン */ import java.util.regex.Pattern; import java.util.regex.Matcher; class JSample1_1{ public static void main(String args[]){ String str1 = "ColorBlack is #000000"; String str2 = "ColorBlue is #0000ff"; String regex1 = "ColorB"; Pattern p1 = Pattern.compile(regex1); String regex2 = "Color(?=Black)B"; Pattern p2 = Pattern.compile(regex2); System.out.println("パターン : " + regex1); check(p1, str1); check(p1, str2); System.out.println("パターン : " + regex2); check(p2, str1); check(p2, str2); } private static void check(Pattern p, String target){ Matcher m = p.matcher(target); if (m.find()){ System.out.println("○ " + target); System.out.println(" " + m.group()); }else{ System.out.println("× " + target); } } }上記をコンパイルした後で実行すると次のように表示されます。 [xxxxxxxx@dddddddddd LookAhead]$ javac JSample1_1.java [xxxxxxxx@dddddddddd LookAhead]$ java JSample1_1 パターン : ColorB ○ ColorBlack is #000000 ColorB ○ ColorBlue is #0000ff ColorB パターン : Color(?=Black)B ○ ColorBlack is #000000 ColorB × ColorBlue is #0000ff [xxxxxxxx@dddddddddd LookAhead]$ |
先読みには否定の先読みも用意されています。否定の先読みを行うパターンは"(?!"から")"の間に記述します。 "パターン(?!先読みパターン)" "(?!先読みパターン)パターン"この場合は、先読みパターンに記載したパターンに一致しない場合に"(?!先読みパターン)"の部分がマッチします。 まず先読みパターンの場合の例を考えてみます。 ターゲット文字列 "ColorBlack is #000000" "ColorBlue is #0000ff" パターン "Color(?=Black)B"通常の先読みパターンであれば、"Color"の後に"B"が続くもので条件として"B"は"Black"と続くものという意味になりますので、"ColorBlack is #000000"にはマッチしますが"ColorBlue is #0000ff"にはマッチしません。 続いて否定の先読みパターンの場合の例を考えてみます。 ターゲット文字列 "ColorBlack is #000000" "ColorBlue is #0000ff" パターン "Color(?!Black)B"否定の先読みパターンであれば"、"Color"の後に"B"が続くもので条件として"B"は"Black"以外のものという意味になりますので、ColorBlack is #000000"にはマッチしませんが"ColorBlue is #0000ff"にはマッチします。 例えば次のような使い方ができます。 "(?!0000)\d\d\d\d"上記のパターンでは、"0000"ではない4桁の数字にマッチします。 なお先読みも否定先読みも位置にマッチするものなので、次のように複数の先読みパターンを記述することができます。 "(?!0000)(?!9999)\d\d\d\d"上記のパターンでは、"0000"及び"9999"ではない4桁の数字にマッチします。 では実際に試してみます。 サンプルプログラム下記のサンプルを実行してみよう。/** * 否定の先読み */ import java.util.regex.Pattern; import java.util.regex.Matcher; class JSample2_1{ public static void main(String args[]){ String str1 = "No.8710"; String str2 = "No.0000"; String regex1 = "\\d\\d\\d\\d"; Pattern p1 = Pattern.compile(regex1); String regex2 = "(?!0000)\\d\\d\\d\\d"; Pattern p2 = Pattern.compile(regex2); System.out.println("パターン : " + regex1); check(p1, str1); check(p1, str2); System.out.println("パターン : " + regex2); check(p2, str1); check(p2, str2); } private static void check(Pattern p, String target){ Matcher m = p.matcher(target); if (m.find()){ System.out.println("○ " + target); System.out.println(" " + m.group()); }else{ System.out.println("× " + target); } } }上記をコンパイルした後で実行すると次のように表示されます。 [xxxxxxxx@dddddddddd LookAhead]$ javac JSample2_1.java [xxxxxxxx@dddddddddd LookAhead]$ java JSample2_1 パターン : \d\d\d\d ○ No.8710 8710 ○ No.0000 0000 パターン : (?!0000)\d\d\d\d ○ No.8710 8710 × No.0000 [xxxxxxxx@dddddddddd LookAhead]$ |
後読みを行うパターンは"(?<="から")"の間に記述します。 "パターン(?<=後読みパターン)" "(?<=後読みパターン)パターン"後読みのパターンは、指定されたパターンがターゲット文字列の前にさかのぼって存在するかどうかを確認し、見つかった場合は見つかった文字列の最後の位置にマッチします。つまり後読みのパターンは位置にマッチするパターンです。次の例を見てください。 ターゲット文字列 "PaddingWidthSize is 30px" パターン "Width(?<=PaddingWidth)Size"この場合、まずパターンの中の"Width"がターゲット文字列の中の"Width"にマッチし、残るターゲット文字列は"Size is 30px"となります。 Padding Width Size is 30px ------- ----- ------------ Width続いてパターンの中の"(?<=PaddingWidth)"がマッチするかどうか試します。後読みの場合は残っているターゲット文字列ではなく、既に見た部分の文字列をさかのぼって対象とします。今回はマッチします。後読みの場合は位置にマッチするだけなので、残っているターゲット文 字列は"Size is 30px"のままです。 PaddingWidth Size is 30px ------------ ------------ Width PaddingWidthつまり"Width(?<=PaddingWidth)Size"のパターンは、"Width"の後に"Size"が続く文字列にマッチするが、条件として"Size"の前には"PaddingWidth"が無ければならないということです。 この時パターン全体にマッチした文字列は"WidthSize"となります。後読みパターンの部分はあくまで位置にマッチしているだけのため、マッチした部分文字列には含まれてきません。 もしターゲット文字列が"MarginWidthSize is 30px"だったら、後読みパターンにマッチしないためパターン全体もマッチしません。またパターン文字列が"WidthSize"だったらターゲット文字列が"PaddingWidthSize is 30px"であっても"MarginWidthSize is 30px"であってもマッチします。 先読み同様に後読みも分かりにくいですが便利な機能なので是非理解しておいて下さい。 否定の後読み後読みの場合も否定の後読みが用意されています。書式は次の通りです。"パターン(?<!後読みパターン)" "(?<!後読みパターン)パターン"基本的な考え方は同じで、後読みパターンに記載したパターンに一致しない場合に"(?<!後読みパターン)"が一致します。 では実際に試してみます。 サンプルプログラム下記のサンプルを実行してみよう。/** * 後読みを使ったパターン */ import java.util.regex.Pattern; import java.util.regex.Matcher; class JSample3_1{ public static void main(String args[]){ String str1 = "MarginWidthSize is 20px"; String str2 = "PaddingWidthSize is 30px"; String regex1 = "Width"; Pattern p1 = Pattern.compile(regex1); String regex2 = "Width(?<=PaddingWidth)Size"; Pattern p2 = Pattern.compile(regex2); System.out.println("パターン : " + regex1); check(p1, str1); check(p1, str2); System.out.println("パターン : " + regex2); check(p2, str1); check(p2, str2); } private static void check(Pattern p, String target){ Matcher m = p.matcher(target); if (m.find()){ System.out.println("○ " + target); System.out.println(" " + m.group()); }else{ System.out.println("× " + target); } } }上記をコンパイルした後で実行すると次のように表示されます。 [xxxxxxxx@dddddddddd LookAhead]$ javac JSample3_1.java [xxxxxxxx@dddddddddd LookAhead]$ java JSample3_1 パターン : Width ○ MarginWidthSize is 20px Width ○ PaddingWidthSize is 30px Width パターン : Width(?<=PaddingWidth)Size × MarginWidthSize is 20px ○ PaddingWidthSize is 30px WidthSize [xxxxxxxx@dddddddddd LookAhead]$ |
|