＃19　正規表現を読み解いてみる

## はじめに
とあるjsonに含まれる文字列をまとめて置換したい！ということで、正規表現を用いて文字列を置換する処理について、前後半にわけて備忘録としてまとめていきたいと思います。
今回はその前半として、処理の基本となる「正規表現」について基本となる知識をまとめていきます。

## 正規表現とは
特定の文字列の集合やパターンを表す汎用的な表記法で、「regular expression」の訳語です。「正則表現」などと訳されることもあります。

> 正規表現などで表される文字列パターンに合致する文字列を探すことを、 パターンマッチと言います。 Java、Perl、Ruby、Python などの言語は正規表現を扱うことができるため、 パターンマッチを行うプログラムを容易に作成することができます。

引用：https://www.mlab.im.dendai.ac.jp/~yamada/java/regex/

文字列のパターンを指定することで「検索」「抽出」「置換」などの作業を効率的に行うことができます。上ではプログラム作成について触れていますが、正規表現に対応したエディタであれば、正規表現とエディタの機能を組み合わせた活用もできるので、知っておいて損はないと思います。



## 例から読み解く正規表現

以下では正規表現の例を取り上げ、記されている文字列のパターンごとに分解して見ていくことで、正規表現への理解を深めていきたいと思います。

### 日付(YYYY-MM-DD形式)
```
[0-9]{4}-[0-9]{2}-[0-9]{2}
\d\d\d\d-\d\d-\d\d
```
上記はどちらもYYYY-MM-DD形式の日付の文字列パターンです。

YYYY-MM-DD形式の日付とは「2020-07-01」のような文字列で表されたものを指します。

この例からは、以下について見ていきます。

 - 数字の定義済み文字クラス
 - 繰り返しのメタ文字
  
※メタ文字とは？
> メタ文字とは、その文字本来の意味とは異なり、プログラムで特別な意味を持たせた文字のことである。
> 例えば、正規表現では「+」は「足す」ではなく1回以上の連続文字を意味するメタ文字である。(中略)メタ文字は、それぞれの環境や機能の中で任意に意味が決められるものであるため、当の環境において各メタ文字は何を意味するのかを、あらかじめ把握しておく必要がある。

引用：https://www.sophia-it.com/content/%E3%83%A1%E3%82%BF%E6%96%87%E5%AD%97

では実際に分解していきましょう。
  
**[0-9]**
これは「０から９までの半角数字の内、どれかひとつ」を表します。
[ ]の中にあるものでいずれか１文字、がより汎用的な理解と言えますね。
そのため、[a-z]であれば「aからzまでのアルファベットのどれかひとつ」となります。
ちなみに[a-z]に大文字は含まれないので、大文字を指定したい場合は[A-Z]とします。
 
**\d**
これも「０から９までの半角数字の内、どれかひとつ」を表します。
\d = [0-9]  と言えますね。

**{4}**
これは「直前の文字を4回繰り返す」ことを表しています。
ここでは[0-9] {4}となっているため、「[0-9]が４つ」=「何かしらの4桁の数字」と言えますね。
{ n } で直前の文字をn回繰り返す、がより汎用的な理解と言えます。
今回の例であれば、「\d{4}-\d{2}-\d{2}」としても同義です。

### メールアドレス
```
[\w\-._]+@[\w\-._]+
```
Gmailなどの一般的なアドレスを想定した簡易的な正規表現です。
この例からは、以下について見ていきます。

 - 単語構成文字の文字クラス
 - メタ文字のエスケープ

※エスケープとは？
直後のメタ文字が持つ特別な意味を無効化させることを指します。正規表現で「+」は1回以上の連続文字を意味するメタ文字ですが、エスケープさせることで本来の意味として処理することができるようになります。

では実際に分解していきましょう。

**\w**

これは定義済み文字クラスのひとつであり「小文字と大文字すべてのアルファベット・０～９の数字」を示します。[a-zA-Z0-9]という表記法もありますが、意味は同じです。

\w = [a-zA-Z0-9]

と言えます。

  

**[-]**

これまで[0-9][a-z]など取り上げてきましたが、実は[ ]内で使用される「-（ハイフン）」は文字の文字コード上の範囲を指定するメタ文字です。「-（ハイフン）」ではなく、「0～9」「a～z」の「～」という意味合いで使用されていました。
そして「\（バックスラッシュ）」はメタ文字を無効化させるエスケープ文字です。そのため、ここでは本来の意味である「-（ハイフン）」として文字クラス内に存在することが示されています。

ちなみに、[ ]の外で使用される「-」や[ ]の中でも文字コード上の範囲を示すものとして不適な「-」はメタ文字ではないので、「-（ハイフン）」として処理されます。
慣れない内は少しややこしいかもしれません。

  

■メタ文字として処理：

     ・ [0-9]　0～9の内、どれかひとつの数字

■ハイフンとして処理：

     ・ -[09]　「-（ハイフン）」と0か9どちらかの数字（計2文字）
     ・ [0\-9]　0か9か「-（ハイフン）」の中からどれかひとつの文字（計1文字）

■メタ文字とハイフンの処理が混合している例：

      [0-9-]　0～9の数字と「-（ハイフン）」の中からどれかひとつの文字（計1文字）

  

**[.]**

「.（ピリオド）」は「任意の一字」を意味するメタ文字です。ただし、[ ]内ではメタ文字ではなく、本来の意味である「.（ピリオド）」として処理されます。
先にご紹介した「-」とちょうど逆の動きですね。

  

■メタ文字として処理：　

     ・ .[ABC]　・・・「.（任意の一字）」とA、B、Cの中からどれかひとつの文字（計2文字）


■ピリオドとして処理：　

 

    ・ [.ABC]　・・・「.（ピリオド）」、A、B、Cの中からどれかひとつの文字（計1文字）

  

**[_]**

「_（アンダースコア、下線符号）」はメタ文字ではないので、そのままの意味で処理されます。

  

**+**
先に少しお話しているように、「直前の文字を1回以上繰り返す」という意味を持つメタ文字です。
この例での直前の文字は`[\w\-._]`ですね。
なので`[\w\-._]+`は「0～9の数字、小文字のアルファベット、大文字のアルファベット、ハイフン、ピリオド、アンダースコア」のいずれか1文字が1回以上繰り返される場合の正規表現と言えます。

  

**@**

「@（アットマーク）」はメタ文字ではないので、そのままの意味で処理されます。

  

では、例として挙げたメールアドレスの正規表現をもう一度確認してみましょう。

    [\w\-._]+@[\w\-._]+

これを言葉にしてみると、

「0～9の数字、小文字のアルファベット、大文字のアルファベット、ハイフン、ピリオド、アンダースコア」のいずれか1文字が1回以上繰り返された後、「@」が続き、更に「0～9の数字、小文字のアルファベット、大文字のアルファベット、ハイフン、ピリオド、アンダースコア」のいずれか1文字が1回以上繰り返される文字列

ということになります。

ただし、実際のメールアドレスでは「.」を末尾にして使用することができないように、今回取り上げた例が完璧なメールアドレスの正規表現であるとは言えません。

正規表現を読み解くことができれば、足りないものや要らないものを必要に応じて自由にカスタマイズすることもできますね。

  

## おわりに

今回はjsonファイルの文字列を変換する処理を作成するにあたり、必要となる正規表現について取り上げました。とても初歩的な内容となっていますので、正規表現にあまりなじみのなかった方がこれを機に興味を持ってもらえたら幸いです。

今回紹介した正規表現は、例に関わるものだけに絞られています。この他にも便利な表現記法が様々あるので、気になった方はぜひ調べてみてください。

以上です。閲覧ありがとうございました。

  

出典：

 - https://docs.oracle.com/javase/jp/16/docs/api/java.base/java/util/regex/Pattern.html
 - https://qiita.com/grrrr/items/7c8811b5cf37d700adc4
 - https://www.javadrive.jp/regex-basic/sample/index13.html