正規表現について
イートスマートの新人エンジニアが、正規表現について学んだことを振り返ってみます。
正規表現について
文字列のデータの中から、特定のパターン(文字の組み合わせ)にマッチするかを調べ るために用いられる表現です。文字列の完全一致だけでなく、より複雑な条件を指定し 例えば先頭の文字がaで始まり、末尾がbで終わる文字列の一致などをみることができます。
実装の内容
もぐナビの食品に関する情報について、スマホサイト側で単位当たりのカロリー表示を 出すため変更が必要となりました。 その際に、補足説明を表すカラムの内容を出せば一発解決ですが、全ての内容を出力するには問題があったため正規表現を使って対応しました。
正規表現に関する実装例
public String getCalorieUnit { //判定する文字列を変数に定義する String d = description; //判定する文字列のパターンを指定する String regex = "栄養成分.*(?<=あたり|当たり|当り|g|)"; ※ //正規表現のパターンをコンパイルする Pattern p = Pattern.compile(regex); if(CheckUtil.isNull(d)) { return ""; } //正規表現を利用したい文字列を渡してオブジェクトを作成 Matcher m = p.matcher(d); //カロリーの値がnullではなく、かつ指定したパターンに一致した場合trueが返る if (calorie != null && m.find()) { //group()メソッドでマッチした部分の文字列が取得できる return m.group(); } return ""; }
使用した正規表現※について
今回は、規則性として「栄養成分~」から始まり、あたり(当たり、当り)、 mg,gなどで終わるパターンを想定しています。 (「g」を入れているのは、例えば「栄養成分100mlあたり※カフェイン約10mg」というデータが存在したため。)
まず、この表現の中で用いられている「.」任意の1文字を指します。 「*」は直前の文字を0回以上繰り返すという意味になります。 ここまでで、「〇〇栄養成分〇〇」という文字列に合致したものがマッチします。 「?<=~」で文字列が~に該当した場合にだけ一致するという条件をつけます。 ~の場合は、複数のワードが入る可能性があるため、前後のどちらかに一致するかを 表す「|」で該当のワードを複数列挙しています。 以上から、「〇〇栄養成分〇〇あたり」に該当するようなデータがヒットするようになります。
テストコードの一例
public void test_getCalorieUnit() { BigDecimal bd = BigDecimal.valueOf(250); FoodBean food = new FoodBean(); food.setCalorie(bd); food.setDescription("栄養成分1食320gあたり※関東限定"); //販売地域に関する情報が不要なため、それらを除外した形で出力できているかをテスト assertThat(food.getCalorieUnit(), is("栄養成分1食320gあたり")); food.setDescription("***栄養成分100ml当たり"); //栄養成分という文字列が文の途中で現れる場合を想定 assertThat(food.getCalorieUnit(), is("栄養成分100ml当たり")); food.setCalorie(null); //カロリー情報がない場合には空で返却されているか assertThat(food.getCalorieUnit(), is("")); }
正規表現とワイルドカードについて(参考)
ワイルドカードは、正規表現と同様に文字列のパターンを表すことができます。 例えばワイルドカードでは「 * 」は0文字以上の文字列,「 ? 」は任意の1文字を指します。 これが正規表現の場合ですと、それぞれ「 .* 」、「 . 」という書き方になります。 ワイルドカードが使われる場面としては、例えばコマンドラインでファイル検索をするときに bashで利用されるものになります。bashなどのシェルでは、正規表現を解釈するようには なっていないため、ワイルドカードで表現することになります。