(更新)"(?"で始まるキャプチャされない括弧を考慮した。
必要な機能のみを実装した簡易なものです。
経緯
どうしても、名前付きキャプチャが必要となったので、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以外の(?の場合はマッチしないようにしました。
