keywords: java, j2se5.0, 正規表現, 文字列置換
忘れそうなのでメモ。
発生した例外
Exception in thread "main" java.lang.IllegalArgumentException: Illegal group reference at java.util.regex.Matcher.appendReplacement(Unknown Source) at java.util.regex.Matcher.replaceAll(Unknown Source) at java.lang.String.replaceAll(Unknown Source) at localtest.Test6.main(Test6.java:16)
原因となるコードと値
package localtest; public class Test6 { public static void main(String[] args) { // 置換対象(元)の文章 String str = "abc001abc002abc003abc003"; // 置換文字列 String rep1 = "ABC"; String rep2 = "DEF"; String rep3 = "H$J"; str = str.replaceAll("001", rep1); str = str.replaceAll("002", rep2); str = str.replaceAll("003", rep3); // Error! System.out.println("000 > " + str); } }
原因と対策
この問題は置換文字列に「$」が入っているため発生する。
このフォームのメソッド呼び出し str.replaceAll(regex, repl) では、次の式と正確に同じ結果が得られます。
Pattern.compile(regex).matcher(str).replaceAll(repl)
String#replaceAll(String, String) (Java 2 Platform SE 5.0)
で、上記の「.replaceAll(repl)」の参照先が以下。
置換文字列内でバックスラッシュ (\) とドル記号 ($) を使用すると、それをリテラル置換文字列として処理した場合とは結果が異なる場合があります。ドル記号は、先に説明したとおり、先方参照された部分シーケンスへの参照として処理される場合があり、バックスラッシュは置換文字列内のリテラル文字をエスケープするのに使用されます。
Matcher#replaceAll(String) (Java 2 Platform SE 5.0)
「$」を文字として使う方法については、自分の場合は単純な文字列置換だったので String#replace(CharSequence, CharSequence) を使うことで事足りました。
正規表現を含めて使いたい場合は文字列の場合だけエスケープ処理しないといかんのかな・・・、そのとき考えよう・・・。
参考サイト
('ω`)
追記:2012/05/24
$
の含まれる位置によっては、発生する Exception が異なるので注意。
package localtest; public class Test6 { public static void main(String[] args) { // 置換対象(元)の文章 String str = "abc001abc002abc003abc003"; // 置換文字列 String rep1 = "ABC"; String rep2 = "DEF"; String rep3 = "HI$"; // 文字列の末尾に'$'がある場合 str = str.replaceAll("001", rep1); str = str.replaceAll("002", rep2); str = str.replaceAll("003", rep3); // Error! System.out.println("000 > " + str); } }
上記コードの場合、以下の例外が発生する。\
を文字列の末尾に入れた場合でも同じ。
Exception in thread "main" java.lang.StringIndexOutOfBoundsException: String index out of range: 3 at java.lang.String.charAt(Unknown Source) at java.util.regex.Matcher.appendReplacement(Unknown Source) at java.util.regex.Matcher.replaceAll(Unknown Source) at java.lang.String.replaceAll(Unknown Source) at localtest.Test6.main(Test6.java:16)
ようは、エスケープ文字を指定しているように見えて記号の次に文字が無いのでエラーとなっているわけね。