正規表現を理解してみる

# はじめに
これは、[静岡 Advent Calendar 2019](https://qiita.com/advent-calendar/2019/shizuoka)の3日目の記事となります。

今年は勉強会には2019/1/26(土)に「DevLOVE静岡」しか参加していないので勉強会のことは書きにくい。
一応、Qiitaは技術系ブログなので静岡にまつわる(名産品や地名など)なら何でもいいかなと、ネタとして2012年に静岡Developers勉強会で「[JavaScriptの正規表現](https://www.slideshare.net/yaju88/javascript-14525756)」のセッションを引っ張り出して、これをQiita用に書き直してみました。

# 正規表現
正規表現ってなに？
正規表現とは、簡単に言えば 検索や置換をより便利にするものです。
例えば文章中に 「りんご」 と 「みかん」 を検索したいとき、別々に検索するのは手間だし面倒です。
正規表現で検索に使うキーワードを以下のように書くと

```
 りんご|みかん
```

「りんご」と「みかん」を同時に検索する事が出来ます。

javascript 正規表現 チートシート
http://visibone.com/products/bbk14-15_850.jpg

## オンライン上の正規表現の確認サイト
* [Online regex tester and debugger: PHP, PCRE, Python, Golang and JavaScript](https://regex101.com/r/q681jI/2/)
![regex101.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/352d0305-125f-a301-4b4a-81eed9c0b66a.png)

* [Regex Tester - Javascript, PCRE, PHP](https://www.regexpal.com/)
![RegExPal.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/03b77af2-7c3b-8611-62af-59a692785dce.png)


## 正規表現で使用する特殊文字
「｜」を使うと複数キーワードで検索できるように、下記の特殊文字には役割がある。

```
 ¥ / [ ] - ( ) ? + * | . ^ $
```
 
正規表現では「メタ文字」と呼んでいる。
メタ文字自身を検索対象とする場合、¥を付ける

```
例 ¥¥ ,¥[ , ¥+
```

## 任意の1文字と位置指定
### 「.」…とにかくなんでもいい１文字(改行以外)
正規表現 み<font color="red">.</font>ん
対象文字 み<font color="red">か</font>ん み<font color="red">し</font>ん み<font color="red">り</font>ん

### 「^」…先頭にマッチ 呼び名：ハット
正規表現 <font color="red">^</font>みかん
対象文字 <font color="red">みかん</font> みかん みかん

### 「$」…末尾にマッチ
正規表現 みかん<font color="red">$</font>
対象文字 みかん みかん <font color="red">みかん</font>

## 同じ文字の繰り返し
### 「*」…直前の文字を 0 回以上にマッチ
正規表現 お<font color="red">ー*</font>い  ※数量詞表現： おー{0,}い
対象文字 <font color="red">おい</font> <font color="red">おーい</font> <font color="red">おーーい</font>

###「+」…直前の文字を 1 回以上にマッチ
正規表現 おー<font color="red">+</font>い ※数量詞表現： おー{1,}い
対象文字 おい <font color="red">おーい</font> <font color="red">おーーい</font>

### 「?」…直前の文字を 0 回又は 1 回にマッチ
正規表現 おー<font color="red">?</font>い ※数量詞表現： おー{0,1}い
対象文字 <font color="red">おい</font> <font color="red">おーい</font> おーーい

### 「.*」 …なんでもいい文字の連続(最長)
正規表現 JR<font color="red">.*</font>駅
対象文字 <font color="red">JR静岡駅から浜松駅</font>まで
※可能な限り合致するまでヒットする

### 「.*?」…なんでもいい文字の連続(最短)
正規表現 JR<font color="red">.*?</font>駅
対象文字 <font color="red">JR静岡駅</font>から浜松駅まで
※可能な限り少ない回数でヒットする

## パターン論理和とパターングループ
### 「|」…いずれかの文字列(論理和)
正規表現 みかん|りんご
対象文字 ばなな <font color="red">みかん</font> ぶどう <font color="red">りんご</font>

### 「()」…グループ化(先優先)
**()を付けた場合の違い**
正規表現 私はみかん|りんごが好きです
対象文字 <font color="red">私はみかん</font>が好きです
対象文字 私は<font color="red">りんごが好きです</font>

正規表現 私は(みかん|りんご)が好きです
対象文字 <font color="red">私はみかんが好きです</font>
対象文字 <font color="red">私はりんごが好きです</font>

## 数詞とパターングループ
### 「{}」…数量詞
正規表現 w{3} 直前の文字を n 回にマッチ
対象文字 <font color="red">www</font>.ora.com/goodparts/ww

正規表現 w{2,} 直前の文字を n 回以上にマッチ
対象文字 <font color="red">www</font>.ora.com/goodparts/<font color="red">ww</font>

正規表現 w{0,3} 直前の文字を n 回以上、m 回以下にマッチ
対象文字 <font color="red">www</font>.ora.com/goodparts/<font color="red">ww</font>

### 「()」…グループ化
正規表現 (じゃ)+ーん
正規表現 <font color="blue">(じゃ){1,}</font>ーん ※数量詞に置き換えた場合
対象文字 <font color="red">じゃ</font>ーん <font color="red">じゃじゃ</font>ーん <font color="red">じゃじゃじゃ</font>ーん

## 数量詞の補足
繰り返し回数
正規表現 (みかん){2,<font color="red">4</font>}
対象文字 <font color="red">みかんみかんみかんみかん</font>みかん
※指定した回数より多く繰り返している場合には 繰り返し回数の上限までマッチする。 

正規表現 (みかん){<font color="red">2</font>,4}
対象文字 <font color="red">みかんみかんみかん</font>
※可能な限り多い回数でマッチする。

正規表現 (みかん){<font color="blue">2</font>,4}
対象文字 <font color="red">みかんみかんみかんみかん</font><font color="blue">みかんみかん</font>
※5 個以上は、パターンの繰り返しでマッチする。

## 文字クラス
### 「[]」…指定内の任意表現 呼び名：ブラケット
正規表現 今日の天気は<font color="red">[晴曇雨]</font>です
対象文字 今日の天気は<font color="red">晴</font>です
対象文字 今日の天気は<font color="red">曇</font>です
対象文字 今日の天気は<font color="red">雨</font>です
対象文字 今日の天気は雪です

### 「[^]」…指定内の任意以外表現
正規表現 今日の天気は<font color="red">[^晴曇雨]</font>です **(^が先頭)**
対象文字 今日の天気は<font color="red">雪</font>です

※[]の中で ^ が使用された場合は、行の先頭を表す ^(ハット)とは意味が異なります。

### [] と (|) の使い方を間違えた場合
例 [りんご|みかん]
「り」「ん」「ご」「|」「み」「か」「ん」のどれか一文字という意味になる。
![regex101_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14053/5786c2b3-da65-602f-18b8-b9a22c2fa765.png)

### 文字クラス内の記述
* 「[A-Z]」…英大文字 A から Z までのどれか
* 「[a-z]」…英小文字 a から z までのどれか
* 「[0-9]」…数字 0 から 9 までのどれか
* 「[A-Za-z0-9]」…上記を連続して書ける
* 「[あ-お]」…あ<font color="blue">ぃ</font>い<font color="blue">ぅ</font>う<font color="blue">ぇ</font>え<font color="blue">ぉ</font>お のどれか
* 「[か-こ]」…か<font color="blue">が</font>き<font color="blue">ぎ</font>く<font color="blue">ぐ</font>け<font color="blue">げ</font>こ のどれか
* 「[.*]」…. か * のいずれかの文字
※[]の中ではメタ文字は普通の文字として認識される文字もメタ文字ではない。
(但し、先頭の＾や ] や範囲指定の‐は例外)

### 主な特殊文字
* 「¥w」…英字、数字、アンダースコア。 [a-zA-Z0-9_] と同じ
* 「¥W」…英字、数字、アンダースコア以外の文字。 [^a-zA-Z0-9_]と同じ
* 「¥d」…数字。[0-9] と同じ
* 「¥D」…数字以外の文字。[^0-9]と同じ。
* 「¥n」…改行
* 「¥s」…スペース(全角スペースも対象)
* 「¥S」…スペース(全角スペースも対象)以外の文字

## 主な正規表現の使用例
### 日付を検索する
正規表現 \d{4}\/\d{1,2}\/\d{1,2}
対象文字 2019/12/20 2019/12/2

### 郵便番号を検索する(厳密ではない)
正規表現 \d{3}-\d{4}
対象文字 012-1234

### メールアドレスを検索する(厳密ではない)
正規表現 ^[\w_-]+@[\w¥.-]+\.\w{2,}$
対象文字 microsoft@e-mail.microsoft.com

## 正規表現の修飾子
### 「/g」…Global (複数回マッチする)
正規表現 /みかん/g
対象文字 <font color="red">みかん</font> りんご <font color="red">みかん</font>

### 「/i」…Insensitive (大文字小文字を区別しない)
正規表現 /ABC/i
対象文字 <font color="red">abc</font>def

### 「/m」… Multiline (^ と $ が行末記号にマッチ)
正規表現 /456/m
対象文字 123¥n<font color="red">456¥n</font>789
※¥n:改行

## 正規表現グループ
多くの正規表現エンジンでは「( )」を使ったキャプチャ(マッチした文字列の捕捉)をサポートしています。「$(番号)」でマッチした文字列を順番に取得できます。

### キャプチャ
```javascript
xx = "12:34:56".match(/(\d+):(\d+):(\d+)/);
document.write(RegExp.$1 + "<br>");    // → 12
document.write(RegExp.$2 + "<br>");    // → 34
document.write(RegExp.$3 + "<br>");    // → 56
```

### 非キャプチャ
「(?:)」を使うことでキャプチャを外します。

```javascript
xx = "12:34:56".match(/(?:\d+):(\d+):(\d+)/);
document.write(RegExp.$1 + "<br>");    // → 34
document.write(RegExp.$2 + "<br>");    // → 56
```

### 肯定先読み
リンカーンの「人民の人民による人民のための政治」というフレーズ
正規表現 人民
対象文字 <font color="red">人民</font>の<font color="red">人民</font>による<font color="red">人民</font>のための政治
人民が3つ抽出されました。

肯定先読みを使用します。
正規表現 人民(?=による)
対象文字 人民の<font color="red">人民</font>による人民のための政治

人民が3つありますが、肯定先読み(?=による)をつけることで「による」が含まれる「人民」だけがマッチしたということです。ここでポイントなのは、マッチした「による」が結果に含まれていないことです。
肯定先読みは後に特定の言葉を含んでいる条件を満たす文字列のみを抽出したいという時に使えます。

参照：[肯定先読み～否定後読み](http://seikihyougenn.com/howto-lookahead.html)

### 否定先読み
肯定先読みの逆になります。「による」が含まれる「人民」以外が抽出されます。

正規表現 人民(?!による)
対象文字 <font color="red">人民</font>の人民による<font color="red">人民</font>のための政治

## 文字列の否定
### [^ABC]*? … 文字1つ1つを除外
正規表現 JR[^静岡]*?駅
対象文字 JR静岡駅 <font color="red">JR浜松駅 JR三島駅</font> JR<font color="blue">岡</font>山駅
これだとJR岡山駅があると対象外となる

### ((?!ABC).)*? … 文字列を除外 (?!ABC).*?に変更
正規表現 JR((<font color="red">?!</font>静岡).)*?駅
対象文字 JR静岡駅 <font color="red">JR浜松駅 JR三島駅</font> JR岡山駅
これだとJR東静岡駅を含めた場合、JR東静岡駅も対象外となってしまう

# 最後に
当初は静岡のIT勉強会について何か書こうと思ったのですが、あまり過去を振り返っても仕方ないし未来志向にしようと。

ここ数年は低活動状態なので勉強会を検索するとかもしてなかったので、いつの間にか静岡でも結構勉強会やってたんだなというのを記事を書く上で検索して今頃知るくらいです。行こうかなと思ったら出勤日でした残念。
"[11月か12月を目処に「人工知能ハンズオン リベンジ」として勉強会を開催しよう](http://yaju3d.hatenablog.jp/entry/2019/09/16/001713)"と思ってたのですが、まだ知識が足りていないことと、仕事でサーバーリプレース作業をしていまして、これが来年6月までに月2,3の土曜出勤(日曜日は予備日)となり実施日も2週間前くらいでないと確定しないので、延び延びになっています。
自分的は絶対やりたいので来年のいつぞやまでお待ちください。