Java正規表現の基本の書き方|抽出・置き換え・エスケープまで

文字列を操作する際、部分一致など文字列の一部分を操作しようとしても 上手く条件を絞ることができなかったことはありませんか? Javaで標準で提供されている正規表現という手法を用いて、効率的な文字列操作を目指しましょう。

正規表現とは?


正規表現とは記号や文字の組み合わせによって条件(パターン)を作成し、一致する文字列を抽出する処理のことです。例えば、以下のリストから価格のみを取り出すプログラムを作るとします。

◇ふわふわのオムライスが800円!
◇2000円という破格!ステーキセット
◇辛口カレー!通常の20倍の辛さでお値段そのままの1000円!

文字列から値段部分の文字列を取り出す場合、どのようなプログラムを組めば良いでしょうか?

「文字列から一文字ずつ取り出し、半角数字か判定する」そう思われた方もいるでしょう。しかしそのためのコードを組むと条件が煩雑になってしまい、コードが冗長になってしまいます。最後の「辛口カレー!通常の20倍の辛さでお値段そのままの1000円!」は「20倍」という文字列が含まれており、どちらが値段の数値か判定するための条件を増やす必要もあります。プログラムで各条件では複雑ですが、人間にとっては「価格」という分かりやすい条件があります。これを上手く条件にできないでしょうか。

「正規化」では文字列から特定の条件に合った文字列を抽出するとき、条件がいくつも考えられる場合でも、一定の「パターン」によって法則化されていることに着目します。その「パターン」を一つの形式によって文字列を抽出する方法です。

Javaでの正規表現の基本ルール

正規表現では記号を組み合わせることでパターンを表現します。

.  改行文字を除く任意の一文字
*  直前の1文字と同じ文字が0文字以上あるとき
^  行の先頭
$  行の末尾
[ ]  指定した1文字。「-」で範囲指定。(A-Z,0-9など)
[^ ]  指定した1文字と不一致。「-」で範囲指定。
+  直前の1文字と同じ文字が1文字以上
?  直前の0または1文字
{ n}  直前の文字と同じ文字がn個続く
{,n}  直前の文字と同じ文字がn個以下続く
{m,}  直前の文字と同じ文字がm個以上続く
{m,n}  直前の文字がm個以上n個以下続く
|  or。前後の条件どちらかと一致
( )  内部の条件をグループ化。

Javaの正規表現での抽出方法

判定プログラムのサンプル

String menu = “”辛口カレー!通常の20倍の辛さでお値段そのままの1000円!””;
String regex = regex = “”[0-9]{3,}円””;
Pattern pattern;
Matcher matcher;
pattern = Pattern.compile(regex);
matcher = pattern.matcher(menu);
System.out.println(“”文字列:””+menu);
System.out.println(regex+””の条件に一致:””+matcher.find());

実行結果

文字列:辛口カレー!通常の20倍の辛さでお値段そのままの1000円!
[0-9]{3,}円の条件に一致:true

解説

String regex = regex = “”[0-9]{3,}円””;

正規化の条件をここで定義します。先ほどのルールに照らし合わせると、[0-9]は0~9の文字、{3,}は直前の文字の繰り返しを3文字以上を意味します。つまり、0~9の文字が連続して3文字以上あるかを確認します。
価格は必ず3桁以上あること、数字文字列であること、数字の後には必ず””円”がつく、という条件の元作成したパターンです。

Pattern pattern;
Matcher matcher;

Patternは正規化のパターン、正規化のルールをJavaで扱えるようにするクラス、
Matcherは正規化行うターゲットとなる文字列を扱うクラスです。

pattern = Pattern.compile(regex);
matcher = pattern.matcher(menu);
各クラス変数のインスタンスを作成します。regexには正規化のパターン文字列、menuにはターゲット文字列を格納しておきます。」

matcher.find()
ターゲット文字列が正規化のパターンに一致するか確認します。戻り値はboolean型で、一致した場合はtrueが返ります。

抽出プログラムの書き方

String[] menu = new String[]{
“”ふわふわのオムライスが800円!””, “”2000円という破格!ステーキセット””, “”辛口カレー!通常の20倍の辛さでお値段そのままの1000円!””
};
String regex = “”[0-9]{3,}円””;
/**** 正規化に必要なクラス変数 ****/
Pattern pattern;
Matcher matcher;

pattern = Pattern.compile(regex);
for(String str:menu){
matcher = pattern.matcher(str);
while (matcher.find()) {
System.out.println(“”「””+str+””」の価格>>>>”” + matcher.group());
}
}

実行結果

「ふわふわのオムライスが800円!」の価格>>>>800円
「2000円という破格!ステーキセット」の価格>>>>2000円
「辛口カレー!通常の20倍の辛さでお値段そのままの1000円!」の価格>>>>1000円

解説

先ほどのリストをを配列に格納し、価格部分を取り出します。

for(String str:menu)
配列「menu」を用いた拡張for分です

while (matcher.find())
menu配列から取り出した値がパターンに一致した場合以下のループを実行します

matcher.group()
findメソッドでターゲット文字列からパターンに一致する部分を抽出します。

Javaでの正規表現の置き換え方法

置き換えプログラムのサンプル

では、すべての製品が1000円均一になった場合を考えましょう。パターンに一致する文字列の一部分を、文字列”1000円”に置換します。置換にはMatcherクラスメソッドを使います。

replaceFirst(置換後文字列)
…最初にパターンに一致した文字列を置換します

replaceALL(置換後文字列)
…パターンに一致した文字列をすべて置換します。

先ほどのwhile分の中を編集します。

while (matcher.find()) {
System.out.println(“”置換前>>>>””+str);
str=matcher.replaceAll(“”1000円””);
System.out.println(“”置換後>>>>””+str);
}

実行結果

置換前>>>>ふわふわのオムライスが800円!
置換後>>>>ふわふわのオムライスが1000円!
置換前>>>>2000円という破格!ステーキセット
置換後>>>>1000円という破格!ステーキセット
置換前>>>>辛口カレー!通常の20倍の辛さでお値段そのままの1000円!
置換後>>>>辛口カレー!通常の20倍の辛さでお値段そのままの1000円!

エスケープ処理はどうする?

正規表現の記述で使われるカッコ ( ) などの記号自身が含まれる文字列を条件にしたい場合、記号をそのまま書いてもパターンと認識されエラーになってしまいます。

正規表現で使われる記号
¥ * + . ? { } ( ) [ ] ^ $ – |

これらの文字をパターンにしたい場合は以下のようにエスケープ処理をします。

カッコ ( ) を条件含める場合

¥( または ¥)

このように記号の先頭に「¥」を付け加えることで記号を文字として認識します。「¥」自体をパターンに含めたい場合は

¥¥

とします。
また、次の以下のようなエスケープシーケンスもあります。

¥0n n=1桁の8進数
¥0nn 2桁の8進数
¥0mnn 3桁の8進数(0<=m<=3)
¥xhh 2桁の16進数
¥uhhhh 4桁の16進数
¥t タブ文字
¥n 改行文字
¥r リターン文字
¥f 用紙送り文字
¥a ベル
¥e エスケープ文字
¥cx x=制御文字コード

正規表現が遅い場合の対処法


正規表現のパターンを正しく定義しないと、同じ処理を繰り返す、無限ループに陥るなどプログラムの動作が遅くなってしまいます。パターン内で条件が矛盾していないか、似た条件を繰り返していないか確認しましょう。

まとめ

いかがでしたか?正規表現を習得すれば、文字列操作の他にデータベースなど様々なデータの扱いで活躍できるようになります。是非習得して活用してください。

おすすめコンテンツ


ページ上部へ戻る