正規表現あれこれ

正規表現のメモをまとめたもの
間違っている所あったら指摘してくれると助かります。

# 正規表現（Regular Expression）とは？
- 文字列をパターン化し、特殊な文字（メタキャラクタ、メタ文字）で表現したもの

例えば、郵便番号を正規表現を使って表すと以下のように表現できる。

```bash:郵便番号の正規表現
\d{3}-?\d{4}
# 3桁の数字 + ハイフン有りor無し + 4桁の数字
```

この「\d」「{}」「?」のような文字のことを __メタキャラクタ（メタ文字）__ という。  

## 正規表現の由来
- 1940年代にさまざまな神経生理学者たちが、神経回路を数学的に説明するために開発した。
- 1956年、数学者 _Stephen Kleene（スティーブン・クリーネ）_ が、その考え方に基づき論文を著述し、初めて正規表現の概念が紹介された。
- ここで「正則集合の代数」を説明するために「正則表現・正規表現(regular expression)」が使用された。

何が言いたかったのかというと、元々は神経回路の研究で開発された数学的な概念だったということ。  
正規表現という若干分かりにくいネーミングはこのせい

参考：[正規表現の由来](https://msdn.microsoft.com/ja-jp/library/cc427944.aspx)

## 何の役に立つ？
- 文字列検索に役立つ
    - grep検索、エディタやExcelの検索機能  
    → 調査、コーディングが捗る
- 文字列置換に役立つ
    - sed、エディタやExcelの置換機能  
    → 文字列の一括修正、一括挿入、一括削除が楽になる
- 入力された文字列が、規定されたパターン通りに入力されているかの確認（バリデーション）に役立つ
    - 郵便番号、電話番号、メールアドレスなどの入力形式のチェックなど

# 正規表現とワイルドカードの違い

最初の頃に正規表現とワイルドカードを勘違いしていたので確認。

そもそも、ワイルドカード（Wild Card）とは何か？

- トランプ用語で，どのカードの代用にもできるカードのこと（ポーカーにおけるジョーカーなど）
- 転じてITにおけるワイルドカードとは、 __任意の文字列__ にマッチする特殊文字
- OSや言語によって解釈が異なる

### ファイル検索におけるワイルドカード
- windowsやUnix/Linuxのシェルでのファイル検索では、以下のワイルドカードが使える

|ワイルドカード|内容|
|:-:|:-:|
|?|任意の1文字にマッチ|
|\*|0文字以上の任意の文字列にマッチ|

music.mp?　→　「music.mp3」「music.mp4」などにマッチ  
\*.txt　→　拡張子が.txtであるもの全てにマッチ  

### SQLにおけるワイルドカード
- SQLのWHERE句におけるLIKE演算子では、以下のワイルドカードが使える

|ワイルドカード|内容|
|:-:|:-:|
|%|0文字以上の任意の文字列にマッチ|
|\_|任意の１文字にマッチ|

# メタキャラクタとは？
- その文字自身とは異なる、特別な意味や役割を持つ文字のこと

ざっくり分類すると、

- 文字を表すメタキャラクタ：.（ドット）, \w, \W, \s, \S, \d, \Dなど
- 繰り返しを表すメタキャラクタ：+, \*, ?, {n}, {n,m}, {n,}
- 位置を表すメタキャラクタ：^, $, \b, \B
- 文字集合やグループを作るメタキャラクタ：[], [^], |, (), (?:)
- その他（後方参照など）：\1, \2, \3, ... 

※正規表現の実装によってサポートされているメタキャラクタが異なるので注意

## 文字を表すメタキャラクタ

|メタキャラクタ|意味|備考|
|:-:|:-:|:-:|
|.（ドット）|任意の１文字にマッチ|改行にはマッチしない|
|\w|単語を構成する文字にマッチ|wはwordの頭文字|
|\W|単語を構成する文字 __以外の文字__ にマッチ||
|\d|数字（0~9）にマッチ|dはdigitの頭文字|
|\D|数字 __以外の文字__ にマッチ||
|\s|空白文字（半角スペース、タブ文字他）にマッチ|sはspaceの頭文字|
|\S|空白文字 __以外の文字__ にマッチ||

## 制御文字を表すメタキャラクタ

|メタキャラクタ|意味|
|:-:|:-:|
|\t|タブ文字|
|\v|垂直タブ|
|\n|改行文字|
|\r|改行文字（キャレッジリターン）|
|\f|用紙送り文字|
|\a|警告文字|
|\e|エスケープ文字|

## 繰り返しを表すメタキャラクタ（量指定子）

|メタキャラクタ|意味|備考|
|:-:|:-:|:-:|
|{n}|n回以上の繰り返しにマッチ||
|{n,m}|n回以上m回以下の繰り返しにマッチ||
|{n,}|n回以上の繰り返しにマッチ||
|\*|0回以上の繰り返しにマッチ|{0,}と同義|
|+|1回以上の繰り返しにマッチ|{1,}と同義|
|?|0回または1回の繰り返しにマッチ|{0,1}と同義|

## 位置を表すメタキャラクタ（アンカー）
 
|メタキャラクタ|意味|
|:-:|:-:|
|$|行末|
|^|行頭|
|\b|単語境界|
|\B|単語境界以外|

※単語境界とは単語を構成する文字（\w）とそうでない文字（\W）の境目のこと

## 文字集合やグループを作るメタキャラクタ

|メタキャラクタ|意味|
|:-:|:-:|
|[abc]|aかbかcのどれか1文字にマッチ|
|[a-z]|aからzのどれか1文字にマッチ|
|[^abc]|abc以外のどれか1文字にマッチ|
|abc&#x7C;def|abcまたはdefのパターンにマッチ|
|(abc)|グループ化とキャプチャを行う|
|(?:abc)|グループ化を行う|

### [a-z]について
- [a-z]のa-zの範囲は文字コードの範囲
- Unicodeの文字コード番号を使用して[\u0061-\u007A]のように表すこともできる

# その他の正規表現の機能

以下の機能は、処理系によっては使用できない場合があるので注意すること。

## グループ化
- `(abc)`のように括弧内の文字列をひとまとめに扱うこと

例1)  hogehoge....と「hoge」を繰り返す文字列をマッチさせたい場合、以下で検索するとマッチする。
  
```bash:グループ化の例1
(hoge)+  
```

例2)  `https://www.yahoo.co.jp/` と `https://www.google.co.jp/` の両方にマッチさせたい場合、以下で検索するとマッチする

```bash:グループ化の例2
https://www\.(google|yahoo)\.co\.jp/
```

で検索するとマッチする。

## キャプチャ
- `(abc)`括弧内の文字列を記憶し、参照出来るようにすること
- \1 ,\2 ,\3...で前方で記憶した文字列をマッチさせることが出来る
- \$1 ,\$2 ,\$3...で記憶した文字列を変数として参照できる（置換にも使える）

※使用出来るかどうかが言語によって異なるので注意

例1)  hoge_hoge のように、前後が同じものをマッチさせたい

```bash:キャプチャのの例1
# 検索文字列
(.+)_\1
```
で検索するとマッチする。このように前方でマッチしたものを参照することを __後方参照__ という。

例2)  `<a>https://www.google.co.jp/</a>` のaタグ内の値をhref属性に設定したい場合、

```bash:キャプチャの例2
# 検索文字列
<a>(.+?)</a>

# 置換文字列
<a href="$1">$1</a>
```
で置換できる。

例3)  `hoge@fuga.com`からのローカル部、ドメイン部、メールアドレス全体を取り出したい場合、

```bash:キャプチャの例3
# 検索文字列
((.+)@(.+))

# 置換文字列
メールアドレス=$1\nローカル部=$2\nドメイン部=$3
```
で置換すると、以下の結果になる。

```txt:例3の結果
メールアドレス=hoge@fuga.com
ローカル部=hoge
ドメイン部=fuga.com
```

キャプチャする順番は「(」が現れた順になっている。

### キャプチャしない括弧

`(?:)`を使用すると、グループ化のみを行いキャプチャを行わない

- グループ化のみが目的であることを明示したい場合このように書く
- 若干読みにくくなる（可読性が下がる）ので、どちらで書いた方がいいかは状況による

```bash:キャプチャしない括弧の例
https://www\.(?:google|yahoo)\.co\.jp/ 
```

## 先読みと後読み

|先読み・後読み|正規表現|意味|
|:-:|:-:|:-:|
|肯定先読み(positive look forward)|(?=abc)|<font color="red">前方</font>を見てabcが<font color="orange">ある</font>位置にマッチ  |
|否定先読み(negative look forward)|(?!abc)|<font color="red">前方</font>を見てabcが<font color="cornflowerblue">ない</font>位置にマッチ  |
|肯定後読み(positive look behind)|(?<=abc)|<font color="blue">後方</font>を見てabcが<font color="orange">ある</font>位置にマッチ  |
|否定後読み(negative look behind)|(?<!abc)|<font color="blue">後方</font>を見てabcが<font color="cornflowerblue">ない</font>位置にマッチ  |

※JavaScript系では後読みは実装されていないので注意

```java:例
customer.fullname
customer.firstName
customer.lastName
employee.firstName
employee.lastName
officer.firstName
officer.lastName
```

1. customer(?=\\.firstName)
    →後ろにfirstNameが続くcustomerにマッチ

2. customer(?!\\.firstName)
    →後ろにfullNameが来ないcustomerにマッチ

3. (?<=customer\\.)firstName
    →customerの後ろのfirstNameにマッチ

4. (?<!customer\\.)firstName
    →customer以外の後ろのfirstNameにマッチ

### ～を含む・含まない
- 先読み・後読みを応用すると、～を含む・含まない行は以下の正規表現のように表せる

```bash:abcを含む行・含まない行
# abcを含む行
^(?=.*abc).*

# defを含まない行
^(?!.*def).*
```

例)  
スペースを含む行を削除したい  

```bash:スペースを含む行の削除
# 検索文字列
^(?=.* ).*\n

# 置換文字列は空文字
```

## 修飾子
- 多くの処理系にはマッチングの処理方法を変更する指定オプションが用意されている
- 末尾に付ける修飾子をフラグ（flag）と呼ぶ

```JavaScript:JavaScript
// 修飾子iは大文字と小文字を区別しない（ignoreCase）
// 修飾子gは全てのマッチング箇所を検索する（global）
var regex = /hoge/ig;
```

# 正規表現における注意点

## 最長マッチと最短マッチ

思ったよりも長い範囲にマッチしてしまう・・・という時に確認

- 繰り返し文字はデフォルトではマッチしうる最大の文字列にマッチする
- 繰り返しのメタキャラクタの後ろに __「?」__ を付けるとマッチしうる最小の文字列にマッチする

例えば、「hoge@hoge.com」からhogeを別々に2つマッチさせたいとする。

正規表現：`h.*e`

とすると、「hoge@hoge」にマッチしてしまうのでNG、

正規表現：`h.*?e`

のようにする必要がある。

## 基本正規表現と拡張正規表現

あれ？正規表現は合ってるのにマッチしない・・・という場合はそもそも機能がサポートされていない可能性がある

- POSIX正規表現には2種類あり、利用できるメタキャラクタや機能が異なる

|種類|特徴|対応|
|:-:|:-:|:-:|
|基本正規表現<br>(basic regular expression:BRE)|grep, sed など|「&#x7C;」「+」「?」は使用不可<br>「()」「{}」はエスケープが必要|
|拡張正規表現<br>(extended regular expression:ERE)|egrep（grep -E）, sed -E など|「+」「?」は使用できる<br>「()」「{}」はエスケープ不要|

- さらにこれに機能を加えた正規表現があり、追加機能が使えるかどうかは実装毎に確認するしかない。


参考：[3つの正規表現を知らないとハマるヨ](http://jmatsu.hatenablog.com/entry/2017/01/30/130952)

## エスケープの必要性

コード内で正規表現を使用する場合には、エスケープが２重に必要な場合があるので注意

```java:java
// 文字列リテラル
String regex = "hoge\\.com";

// バックスラッシュの正規表現は\\\\と書く。これで「\」のみにマッチ
String backslashRegex = "\\\\";
```

### 正規表現リテラル
- 正規表現リテラルにはそのまま正規表現を書ける（正規表現のみのエスケープで良い）。
- JavaScriptやRubyなどは正規表現リテラルがサポートされているが、Javaはサポートされていない

```JavaScript:JavaScript
// 正規表現リテラル
var regex = /hoge\.com/;
```

# 正規表現のチェックに役立つサイト
- [Regexper：正規表現を可視化して確認できる](https://regexper.com/)
- [regex-testdrive：Javaの正規表現チェック](http://regex-testdrive.com/ja/)
- [okumocchi：PHP及びJavaScript正規表現チェック](http://okumocchi.jp/php/re.php)
- [Regexr：JavaScriptの正規表現チェック](http://regexr.com/)
- [Rubular：Rubyの正規表現チェック、キャプチャ内容がわかる](http://rubular.com/)

# 補足・参考

### 「\\」（バックスラッシュ）と「¥」（円マークについて）

- Unicodeが広まる以前、和文フォントでは「/」の文字コード（0x5c）に「¥」の表記を割り当てていたため、「\」を入力すると「¥」が表示されるようになった。  
- その名残で「\\」文字コード（0x5c）を入力した場合、「¥」が表示される場合がある。  
- Unicodeでは「\\」の文字コード（U+005c）とは別に、「¥」に文字コード（U+00A5）が割り当てられている。

### 改行コードについて
正規表現とは関係ないけどキャレッジリターンってなんぞ？と思ったので調べてみた

- 「CR」「LF」「CRLF」の3種類ある
- 昔のMacではCR,現在のMacやLinuxなどのUnix系ではLF,windowsではCRLFを採用している

>タイプライターにはキャリッジというロールに紙を挟む役目を持つ部品があり、その位置を調節する「キャリッジリターンレバー」と、1行分ロールを回し紙を送ること（ラインフィード）が出来るレバーを順に操作することで改行を行っていた。

#### キャリッジリターン（carriage return:CR、復帰）
- カーソルを行の先頭に戻す指示（左に移動）

#### ラインフィード（line feed:LF, newline, end-of-line、狭義の改行）
- カーソルを一つ下の行に移す指示（下に移動）
- 以前はカーソルを下に移すだけで行頭には戻らなかった（現在は行頭に戻るようになっている）

### grep
- grepとはedというラインエディタのコマンド g/re/pのコマンドからきている
- global Regular Expression printの略

### バリデーション（validation）とは？
- validate：検証する・（妥当性を）確認する、有効性を確かめる
- IT分野では「規定された文法・仕様通りに記述されているかどうかを検証すること」
- メールアドレス、パスワード、電話番号、郵便番号など文法のチェックの意味で使われる