|
|
リージョン |
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 にアップされています。一部、上位互換について、見直しを行っていきます。 |
|
まずはリージョンを設定し、マッチするかどうかの対象となるターゲット文字列の範囲を設定する方法を確認します。 正規表現を使う手順は今まで次のように行っていました。 String str = "abcdef"; String regex = "abc"; Pattern p = Pattern.compile(regex); Matcher m = p.matcher(str); if (m.find()){ System.out.println("マッチしました"); }パターンを定義し、ターゲット文字列とパターン文字列からパターンオブジェクトを作成してから、今度はマッチャオブジェクトを作成しました。 ではターゲット文字列の中の一部の範囲だけを対象にする場合にどの段階で指定を行うのかというと、マッチャに対して範囲を指定します。同じマッチャに対してリージョンを設定したり、それをリセットしたりすることが可能です。 ではリージョンを設定してみます。リージョンの設定を行うにはMatcherクラスで定義されているregionメソッドを使用します。 region public Matcher region(int start, int end)正規検索エンジンの領域に制限を設定します。領域は、マッチの検索対象となる入力シーケンスの一部です。このメソッドを呼び出すと、正規検索エンジンがリセットされ、領域の先頭が start パラメータにより指定されたインデックスに、領域の末尾が end パラメータにより指定されたインデックスにそれぞれ設定されます。 使用される透明度とアンカー設定によっては (useTransparentBounds および useAnchoringBounds を参照)、アンカーなどの特定の作成上の振る舞いが領域の境界またはその付近で異なる場合があります。 パラメータ: start - 検索を開始する位置のインデックス (その値も含む) end - 検索を終了する位置のインデックス (その値を含まない) 戻り値: この正規表現エンジン 例外: IndexOutOfBoundsException - start または end がゼロより小さい場合、start が入力シーケン スの長さより大きい場合、end が入力シーケンスの長さより大きい場合、または start が end より大きい場合 1番目の引数には開始インデックス、2番目の引数には終了インデックスを指定して下さい。例えば先頭の文字はインデックス0、次の文字はインデックス1となります。region(0, 1)と指定すれば、0番目の文字から1番目の文字となりますが、開始インデックスの文字は含みますが終了インデックスの文字は含みませんので結果的に0番目の文字だけが対象となるリージョンとなります。 なおregionメソッドを実行するとマッチャがリセットされます。findメソッドを連続で使っている途中で実行する場合は注意して下さい。 次の例を見てください。 String str = "border width is 100px, and border height is 80px"; String regex = "\\d+"; Pattern p = Pattern.compile(regex); Matcher m = p.matcher(str); if (m.find()){ System.out.println(m.group()); }パターンは数字が1文字以上続くものですので、上記の場合はパターンがマッチし、マッチした部分は"100"となります。 次にリージョンを設定します。 String str = "border width is 100px, and border height is 80px"; String regex = "\\d+"; Pattern p = Pattern.compile(regex); Matcher m = p.matcher(str); m.region(21, 48); if (m.find()){ System.out.println(m.group()); }パターンは先ほどと同じですがリージョンが21文字目から48文字目までに設定されています。よってマッチするかどうかの対象となる文字列は", and border height is 80px"となります。結果として上記の場合はパターンがマッチし、マッチした部分は"80"となります。 リージョンの設定が有効となるメソッド有効: find() lookingAt() matches() 無効: find(int start) replaceAll(String replacement) replaceFirst(String replacement)引数有りのfindメソッドと置換を行うreplaceAllメソッドとreplaceFirstメソッドは、メソッドが実行されるとまずリセットが行われます。その為、事前にリージョンを設定していも意味を持ちません では実際に試してみます。 サンプルプログラム下記のサンプルを実行してみよう。/** * リージョンの設定 */ import java.util.regex.Pattern; import java.util.regex.Matcher; class JSample1_1{ public static void main(String args[]){ String str = "border width is 100px, and border height is 80px"; String regex = "\\d+px"; Pattern p = Pattern.compile(regex); System.out.println("パターン : " + regex); System.out.println("ターゲット文字列 : \"" + str + "\""); Matcher m = p.matcher(str); int start = m.regionStart(); int end = m.regionEnd(); System.out.println("\nデフォルトのままマッチ:"); System.out.println("region : \"" + str.substring(start, end) + "\""); if (m.find()){ System.out.println("match:" + m.group()); }else{ System.out.println("×"); } /* マッチャをリセットしリージョンを設定 */ start = str.indexOf(','); end = str.length(); m.region(start, end); System.out.println("\nリージョン設定(" + start + "," + end + ")"); System.out.println("region : \"" + str.substring(start, end) + "\""); if (m.find()){ System.out.println("match:" + m.group()); }else{ System.out.println("× "); } /* マッチャをリセットしリージョンを設定 */ end = start; start = 0; m.region(start, end); System.out.println("\nリージョン設定(" + start + "," + end + ")"); System.out.println("region : \"" + str.substring(start, end) + "\""); if (m.find()){ System.out.println("match:" + m.group()); }else{ System.out.println("×"); } } }上記をコンパイルした後で実行すると次のように表示されます。 [xxxxxxxx@dddddddddd Region]$ javac JSample1_1.java [xxxxxxxx@dddddddddd Region]$ java JSample1_1 パターン : \d+px ターゲット文字列 : "border width is 100px, and border height is 80px" デフォルトのままマッチ: region : "border width is 100px, and border height is 80px" match:100px リージョン設定(21,48) region : ", and border height is 80px" match:80px リージョン設定(0,21) region : "border width is 100px" match:100px [xxxxxxxx@dddddddddd Region]$ |
現在どの位置にリージョンが設定されているのかを取得する方法を確認します。 リージョンの開始インデックスを取得するにはMatcherクラスで定義されているregionStartメソッドを使用します。 regionStart public int regionStart()この正規検索エンジンの領域の開始インデックスをレポートします。この正規検索エンジンが行う検索は、regionStart (その値も含む) と regionEnd (その値を含まない) の内部でのマッチ検索に制限されます。 戻り値: この正規検索エンジンの領域の始点 リージョンの終了インデックスを取得するにはMatcherクラスで定義されているregionEndメソッドを使用します。 regionEnd public int regionEnd()この正規検索エンジンの領域の終了インデックス (その値を含まない) をレポートします。この正規検索エンジンが行う検索は、regionStart (その値も含む) と regionEnd (その値を含まない) の内部でのマッチ検索に制限されます。 戻り値: この正規表現エンジンの領域の終点 メソッドを実行することで、現在設定されているリージョンの開始インデックスと終了インデックスをそれぞれ取得できます。なおregionメソッドを使って明示的に設定する前は、開始インデックスは文字列の先頭を表す0、終了インデックスは文字列の最後を表す文字列数に設定されています。 デフォルトのregionEndメソッドの戻り値はターゲット文字列の最後なので、開始インデックスだけ指定したい場合は次のように記述することができます。 String str = "border width is 100px, and border height is 80px"; String regex = "\\d+"; Pattern p = Pattern.compile(regex); Matcher m = p.matcher(str); m.region(10, m.regionEnd());また、例えば開始インデックスは現在のままで、終了インデックスだけ別の値を指定する場合は次のように記述できます。 String str = "border width is 100px, and border height is 80px"; String regex = "\\d+"; Pattern p = Pattern.compile(regex); Matcher m = p.matcher(str); m.region(10, 48); /* ... */ Matcher m = p.matcher(str); m.region(m.regionStart(), 24);regionメソッドは常に開始インデックスと終了インデックスの両方を指定する必要があるため、regionStartメソッドとregionEndメソッドを使うと便利です。 では実際に試してみます。 サンプルプログラム下記のサンプルを実行してみよう。/** * リージョンの位置を取得 */ import java.util.regex.Pattern; import java.util.regex.Matcher; class JSample2_1{ public static void main(String args[]){ String str = "border width is 100px, and border height is 80px"; String regex = "\\d+px"; Pattern p = Pattern.compile(regex); System.out.println("パターン : " + regex); System.out.println("ターゲット文字列 : \"" + str + "\""); Matcher m = p.matcher(str); int start = m.regionStart(); int end = m.regionEnd(); System.out.println("\nデフォルトのままマッチ:"); System.out.println("region : \"" + str.substring(start, end) + "\""); if (m.find()){ System.out.println("match:" + m.group()); }else{ System.out.println("×"); } m.region(24, m.regionEnd()); System.out.println("\n開始インデックスを 24 に設定:"); System.out.println("region : \"" + str.substring(24, m.regionEnd()) + "\""); if (m.find()){ System.out.println("match:" + m.group()); }else{ System.out.println("× "); } } }上記をコンパイルした後で実行すると次のように表示されます。 [xxxxxxxx@dddddddddd Region]$ javac JSample2_1.java [xxxxxxxx@dddddddddd Region]$ java JSample2_1 パターン : \d+px ターゲット文字列 : "border width is 100px, and border height is 80px" デフォルトのままマッチ: region : "border width is 100px, and border height is 80px" match:100px 開始インデックスを 24 に設定: region : "nd border height is 80px" match:80px [xxxxxxxx@dddddddddd Region]$ |
リージョンを設定すると、ターゲット文字列の中でマッチするかどうかの対象となる範囲が設定されますが、指定した範囲の文字列に対して行の先頭や行の末尾が存在するように見なされます。 次の例を見てください。 border color is red上記のターゲット文字列に対し、パターン"^color"はマッチしません。このパターンは行の先頭の位置から"color"が現れる場合にマッチするからです。 では先ほどのターゲット文字列に、リージョンを(7, 15)に設定します。すると対象となる文字列は次のようになります。 color is今度はパターン"^color"はマッチします。リージョンの設定によってあたかもターゲット文字列の行の先頭が"color"から始まっているように見えるからです。 以上がデフォルトの動作なのですが、リージョンを設定したことによる境界が行頭や行末としてマッチさせたくない場合はMatcherクラスで定義されているuseAnchoringBoundsメソッドを使用します。 useAnchoringBounds public Matcher useAnchoringBounds(boolean b)この正規表現エンジンの領域境界のアンカーを設定します。 このメソッドに true の引数を指定して呼び出すと、anchoring 境界がこの正規表現エンジンで使用されます。boolean 引数が false の場合は、non-anchoring 境界が使用されます。 アンカー設定境界が使用されると、この正規表現エンジンの領域の境界は、 ^ および $ などのアンカーにマッチします。 アンカー設定境界が使用されない場合は、この正規表現エンジンの領域の境界は、 ^ および $ などのアンカーにマッチしません。 デフォルトでは、正規表現エンジンはアンカー設定領域境界を使用します。 パラメータ: b - アンカー設定境界を使用するかどうかを示す boolean 戻り値: この正規表現エンジン 引数にfalseを設定すると、リージョンを設定しても行の先頭や行の末尾は元々のターゲット文字列のままとなります。デフォルトはtrueとなっています。 リージョンの設定を行っても行の先頭や文字列の先頭などの扱いは元のままにしたい場合にfalseを設定して下さい。 なお、現在の設定を取得するにはMatcherクラスで定義されているhasAnchoringBoundsメソッドを使用します。 hasAnchoringBounds public boolean hasAnchoringBounds()この正規表現エンジンの領域境界のアンカー設定を問い合わせるクエリーを出します。 このメソッドは、anchoring 境界がこの正規表現エンジンで使用される場合は true を返し、そうでない場合は false を返します。 アンカー設定境界の詳細については、 useTransparentBounds を参照してください。 デフォルトでは、正規表現エンジンはアンカー設定領域境界を使用します。 戻り値: この正規表現エンジンがアンカー設定境界を使用する場合は true、そうでない場合は false メソッドを実行すると、現在の設定をboolean型の値として返します。 では実際に試してみます。 サンプルプログラム下記のサンプルを実行してみよう。/** * アンカー境界フラグの設定 */ import java.util.regex.Pattern; import java.util.regex.Matcher; class JSample3_1{ public static void main(String args[]){ String str = "Blackcolor is dark color."; String regex = "^color"; Pattern p = Pattern.compile(regex); System.out.println("パターン : " + regex); System.out.println("ターゲット文字列 : \"" + str + "\""); Matcher m = p.matcher(str); int start = m.regionStart(); int end = m.regionEnd(); System.out.println("\nデフォルトのままマッチ:"); System.out.println("region : \"" + str.substring(start, end) + "\""); if (m.find()){ System.out.println("match:" + m.group()); }else{ System.out.println("×"); } start = 5; m.region(start, end); System.out.println("\nリージョンを(" + start + "," + end + ")に設定:"); System.out.println("region : \"" + str.substring(start, end) + "\""); if (m.find()){ System.out.println("match:" + m.group()); }else{ System.out.println("×"); } m.useAnchoringBounds(false); System.out.println("\nアンカー境界フラグを false に設定:"); System.out.println("region : \"" + str.substring(start, end) + "\""); if (m.find()){ System.out.println("match:" + m.group()); }else{ System.out.println("×"); } } }上記をコンパイルした後で実行すると次のように表示されます。 [xxxxxxxx@dddddddddd Region]$ javac JSample3_1.java [xxxxxxxx@dddddddddd Region]$ java JSample3_1 パターン : ^color ターゲット文字列 : "Blackcolor is dark color." デフォルトのままマッチ: region : "Blackcolor is dark color." × リージョンを(5,25)に設定: region : "color is dark color." match:color アンカー境界フラグを false に設定: region : "color is dark color." × [xxxxxxxx@dddddddddd Region]$ |
リージョンを設定すると、ターゲット文字列の中でマッチするかどうかの対象となる範囲が設定されるため、範囲の前や後の部分は見ることができません。 次の例を見てください。 border color is red上記のターゲット文字列に対し、パターン"\br.."がマッチするの"red"です。このパターンは単語の境界の位置から"r"に続いて2つの文字が現 れる場合にマッチするからです。 では先ほどのターゲット文字列に、リージョンを(2, 19)に設定します。すると対象となる文字列は次のようになります。 rder color is red今度はパターン"\br.."がマッチするのは"rde"となります。リージョンの設定によって"rder"が単語の途中の文字列ではなく単語の先頭のように見えるからです。(単語の境界は行頭と最初の文字の間にマッチします)。 以上がデフォルトの動作なのですが、リージョンを設定してもその領域の前や後の部分を調べることができるようにするにはMatcherクラスで定義されているuseTransparentBoundsメソッドを使用します。 useTransparentBounds public Matcher useTransparentBounds(boolean b)この正規表現エンジンの領域境界の透明度を設定します。 このメソッドに true の引数を指定して呼び出すと、transparent 境界がこの正規表現エンジンで使用されます。boolean 引数が false の場合は、opaque 境界が使用されます。 透明な境界を使用する場合、この正規表現エンジンの領域は、前方、後方、および境界のマッチング作成で透明になります。これらの作成は、マッチが適切かどうかを領域の境界を超えて見ることができます。 不透明な境界を使用すると、この正規表現エンジンの境界は、前方、後方、および境界を超えて検索を試みるマッチング作成で不透明となります。これらの作成では境界を以前にさかのぼって検索できないため、領域外ではいかなるマッチングも失敗します。 デフォルトでは、正規表現エンジンは不透明の境界を使用します。 パラメータ: b - 不透明または透明の領域のどちらを使用するかを示す boolean 戻り値: この正規表現エンジン 引数にtrueを設定すると、リージョンを設定してもその範囲を超えてマッチが適切かどうかを調べるようになります。デフォルトはfalseとなっています。 先ほどの単語の境界を調べる場合の他に、先読みや後読みなどで有効となります。 なお、現在の設定を取得するにはMatcherクラスで定義されているhasTransparentBoundsメソッドを使用します。 hasTransparentBounds public boolean hasTransparentBounds()public boolean hasTransparentBounds() この正規表現エンジンの領域境界の透明度を問い合わせるクエリーを出します。 このメソッドは、transparent 境界がこの正規表現エンジンで使用される場合は true を返し、opaque 境界が使用される場合には false を返します。 透明または不透明の境界の詳細については、useTransparentBounds を参照してください。 デフォルトでは、正規表現エンジンは不透明の領域境界を使用します。 戻り値: この正規表現エンジンが透明な境界を使用する場合は true、そうでない場合は false メソッドを実行すると、現在の設定をboolean型の値として返します。 では実際に試してみます。 サンプルプログラム下記のサンプルを実行してみよう。/** * 透過境界フラグの設定 */ import java.util.regex.Pattern; import java.util.regex.Matcher; class JSample4_1{ public static void main(String args[]){ String str = "Black label."; String regex = "\\bla.+?\\b"; Pattern p = Pattern.compile(regex); System.out.println("パターン : " + regex); System.out.println("ターゲット文字列 : \"" + str + "\""); Matcher m = p.matcher(str); int start = m.regionStart(); int end = m.regionEnd(); System.out.println("\nデフォルトのままマッチ:"); System.out.println("region : \"" + str.substring(start, end) + "\""); if (m.find()){ System.out.println("match:" + m.group()); }else{ System.out.println("×"); } start = 1; m.region(start, end); System.out.println("\nリージョンを(" + start + "," + end + ")に設定:"); System.out.println("region : \"" + str.substring(start, end) + "\""); if (m.find()){ System.out.println("match:" + m.group()); }else{ System.out.println("×"); } m.useTransparentBounds(false); System.out.println("\n透過境界フラグを false に設定:"); System.out.println("region : \"" + str.substring(start, end) + "\""); if (m.find()){ System.out.println("match:" + m.group()); }else{ System.out.println("×"); } } }上記をコンパイルした後で実行すると次のように表示されます。 [xxxxxxxx@dddddddddd Region]$ javac JSample4_1.java [xxxxxxxx@dddddddddd Region]$ java JSample4_1 パターン : \bla.+?\b ターゲット文字列 : "Black label." デフォルトのままマッチ: region : "Black label." match:label リージョンを(1,12)に設定: region : "lack label." match:lack 透過境界フラグを false に設定: region : "lack label." match:label [xxxxxxxx@dddddddddd Region]$ |
|