(更新)"(?"で始まるキャプチャされない括弧を考慮した。
必要な機能のみを実装した簡易なものです。
経緯
どうしても、名前付きキャプチャが必要となったので、Javaでの使用方法を調べたところ、どうやら無い。
無いなら作るしかない、というわけで、自分用に作成。
なお、「名前付きキャプチャ」については、別エントリで詳しく書く(未定)とします。
(分からない方は、こちらのリンクで(おそらく)検索できます。)
ソース
※下記のソースはサンプルであり、テストが完全ではありません。
import java.util.ArrayList; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * 名前付きキャプチャを可能とするMatcher * Patternの生成は、コンストラクタで行なう * * 必要なMethodの簡易実装のため、Interfaceの継承は行わない */ //public class NamedMatcher implements MatchResult{ public class NamedMatcher { /** * Matcherのラッピング */ private Matcher matcher; /** * nameの保存 */ private ArrayList<String> names; /** * キャプチャ名を検索、キャプチャするための正規表現文字列 */ final Pattern namePattern = Pattern.compile("(?<!\\\\)\\(((?=[^?])|(\\?P<(.+?)>)); /** * コンストラクタ * @param regexString 正規表現 * @param text 検索対象の文字列 */ public NamedMatcher(String regexString, String text) { //正規表現文字列の分析 //"("単位に?P<name>を取得し、後で割り当てられるようにnameを保存しておく Matcher nameMatcher = namePattern.matcher(regexString); names = new ArrayList<String>(); int group = 0; while(nameMatcher.find()){ //group(3)に、nameに対応する文字列が取得できている String name = nameMatcher.group(3); if(name == null){ //空文字列の場合は、nameがないため、簡易的にgroupのindexを使用する names.add("__" + String.valueOf(group)); }else{ names.add(name); } group++; } //?P<name>を削除した文字列を生成し、それを正規表現の文字列とする //nameMatcherが"(?P<name>"または、"("に該当するはずなので、resetしてそのままreplaceAllで"("に置換 //"("の数は変わらないため、groupはnamesと同じ数になるはず。 nameMatcher.reset(); String replacedText = nameMatcher.replaceAll("("); Pattern regexPattern = Pattern.compile(replacedText); matcher = regexPattern.matcher(text); } /** * find * @return */ public boolean find(){ return matcher.find(); } /** * nameからgroupを取得する * @param name * @return */ public String group(String name){ //グループの番号を取得する int index = names.indexOf(name); if(index < 0){ //indexがマイナスの場合は、存在しないname return null; } //0は全体を示すgroupなので+1 return group(index + 1); } /** * groupを取得する * @param group * @return */ public String group(int group) { if(group < 0 || matcher.groupCount() < group ){ throw new IndexOutOfBoundsException(); }else{ return matcher.group(group); } } /** * groupの数を取得する * @return */ public int groupCount() { return matcher.groupCount(); } }
使用例
使用方法は、以下のような感じ(このソースは適当です。)
void foo(){ NamedMatcher namedMatcher = new NamedMatcher("(?P<name>[^\t]+)\t(?P<file>[^\t]+)(\t(?P<address>[^\t]+))?", text); if(namedMatcher.find()){ String name = namedMatcher.group("name"); String file = namedMatcher.group("file"); String address = namedMatcher.group("address"); } }
解説
名前付きの名前に相当する部分を保管しておいて、使用する際(group())のindexとひも付けています。
他の良い方法や、間違いがあれば、ぜひご連絡ください。
修正履歴
2014/12/11 14:45 初版公開
2014/12/11 15:50
nameが存在しない場合に変な動作になったので、とりあえず、nullを返却するようにした。
場合によっては、IndexOutOfBoundsException()の方が良いかもしれない。
2014/12/12 11:20 経緯を追記
2014/12/18 8:15
(?で始まる場合は、キャプチャされない(groupにカウントされない)ことを考慮した。
キャプチャ名のキャプチャ処理で、後方一致指定を使用して、(?P以外の(?の場合はマッチしないようにしました。