これだけは憶えておきたい、どこでも使える正規表現

# はじめに

よく正規表現の記号の羅列を見てプログラミングの学習者が苦手意識を持ってしまいがちですが、正規表現が使えるととても便利です。

そして言語を問いません。
PHP、Perl、ruby、python、java、javascript、mysql、postgreSQL、エクセルの VBA、Linux のコマンド、apache の mod_rewrite などなど。基本を覚えればつぶしがききます。

エディタによっては検索や置換にも使えます。
エディタでも使えるんだから膨大なドキュメントの置換処理なんかにも使え、作業効率が上がります。

食わず嫌いにならずに、他人の正規表現のコピペに頼らずに、ぜひ挑戦してみてください。


# 正規表現の種類

UNIX には POSIX という規格があり、「simple regular expressions (SRE)」「basic regular expression (BRE)」「extended regular expression (ERE)」の三種類があります。このうち SRE は廃止される予定で使い所も殆ど無いので覚える必要はありません。BRE や ERE は UNIX や Linux のコマンドで使われている規格です。コマンドを打つ機会がある人は覚える必要があります。

BRE はその名の通りベースとなっている規格で、`ed`、`ex`、`more`、`grep`、`sed`等、正規表現が使えるたいていのコマンドで使われています。

ERE はそれを拡張したものです。正規表現が使えるコマンドで `-E` オプションを付けた場合や、`egrep` コマンド、mysql の `REGEXP` 等で使われている規格です。

Perl はこの ERE をさらに拡張し、かなり細かく検索できるようにしました。Perl がテキスト処理に強いとされる理由の一つです。PHP、Ruby、Python、JAVA等、多くのプログラミング言語はこの Perl の拡張版正規表現を参考にしています。
※各言語で拡張方法が微妙に違いますが、ここでは拡張版の正規表現については触れません。

ここでは「どこでも使える正規表現」として３つの正規表現に共通する記法について書きたいと思います。

参考：[Wikipedia 正規表現](https://ja.wikipedia.org/wiki/%E6%AD%A3%E8%A6%8F%E8%A1%A8%E7%8F%BE)


# 基本構文

正規表現で検索や置換を行う場合、検索の対象となる文字列と検索したいキーワードを表現するパターンがあります。そして大半の場合にそのパターンがどこからどこまでかを表す区切り文字（デリミタという）があります。

```js:javascript
// javascript は //（スラッシュ）がデリミタ。
var pattern = /abc/;
```

パターンにはダイレクトに検索するキーワードを入れてもいいが、あらゆるキーワードを検索しようとすると数回に分けて検索しなければいけなくなり、非効率的になります。そのような場合は「数字のみ」や「アルファベット以外」などのパターンを記号で表したほうが検索が効率的になるし、1回で済みます。パターンを記号で表したものをメタ文字（あるいはメタキャラクタ、正規表現演算子）といいます。

```js:javascript
// 数字のみ
var pattern_number_only = /^[0-9]+$/;
```

メタ文字として使われている記号そのものを検索したい場合が出てくるので、その場合はメタ文字をエスケープします。

```js:javascript
// メタ文字である $（ドル）を検索したい場合は \（バックスラッシュ）でエスケープする。
var pattern_dollar = /\$/;
```


## デリミタ（区切り文字）

デリミタはプログラミング言語の殆どは `//`（スラッシュ）で表すことが多いですが、言語によって違うのでここは各言語のリファレンスを確認してください。以下に一例を挙げます。

```mysql:mysql
-- mysqlはSQL文と区別するためにクォーテーション（ ' や " ）で囲む。
SELECT 'fofo' REGEXP '^f.*$';
```

```apache:apache
# apache はデリミタ無し。空白文字がデリミタになる
RewriteRule ^(.*)$ http://www.google.co.jp/ [R=301,L]
```

```bash:grep
# grep ではクォーテーションで囲っても囲わなくてもいい。囲った方が間違いないけど。
grep -E 21:0+ /path/to/file
grep -E '21:0+' /path/to/file
```

```php:php
// PHP はデミリタ//をさらにクォーテーションで囲う。
preg_match( '/.+/', 'foobarbaz', $matches );
// デリミタは // じゃなくてもいい。
// 英数字、バックスラッシュ、空白文字以外の任意の半角文字が使える。
preg_match( '{.+}', 'foobarbaz', $matches );
```

```ruby:ruby
# ruby は基本的には // で囲う。
/.+/ =~ "foobarbaz"
# %r を使えば PHP 同様に任意の半角文字が使える。
%r{.+} =~ "foobarbaz"
```

```perl:Perl
# Perl も基本は // だけど、m を前置きすると PHP 同様に任意の半角文字が使える。
# デリミタを {}（波括弧）にするとメタ文字をエスケープしなくて済むのでラク。
"Hello World" =~ m{World};
```

## メタ文字

メタ文字には文字そのものを表すアトムとその文字の繰り返し量を表す量指定子、位置を示す位置指定子、グループ化とキャプチャができる包括指定子があります。

正規表現を難しいと思わせるのがメタ文字の数々ですが、ここではどこでも使える基本的なものしか説明しません。頑張って憶えましょう。


### アトム

#### `.`（ドット、ピリオド）

ドットは任意の１文字にマッチします。任意の１文字には非出力文字（改行やタブ、空白）も含まれます。
※言語やオプションによっては改行が含まれない場合があります。

例えば `/.../` は任意の３文字となりますので、`abc` にはマッチするけど `ab` には文字数が足りないのでマッチしません。

なお、検索対象の文字列が `abcd` と４文字以上の場合は前半の `abc` か後半の `bcd` にマッチするので、マッチするということになります。３文字のみにマッチさせたい場合は `/^...$/` と位置指定子が必要です。ご注意ください。

以下は `.`（ドット）と `/`（スラッシュ）のみで日付形式（YYYY/MM/DD）にマッチさせる例です。

```js:javascript
// YYYY/MM/DD 形式にマッチさせる。
var str1 = "2017-4-1";
var str2 = "2017/04/01";
console.log( /....\/..\/../.test(str1) );  // false
console.log( /....\/..\/../.test(str2) );  // true
```


#### 文字クラス `[]`（角括弧、大括弧、ブラケット）

角括弧は文字クラスといって、角括弧内にかかれている全ての文字にマッチします。例えば `[0123456789]` は数字１文字にマッチします。`[0-9]` と `-`（ハイフン）を使えば範囲指定できます。アルファベットの場合は `[a-zA-Z]` と表現します。`[9-0]` と順番を逆に指定するとエラーになりますので注意してください。

さっきの日付形式を文字クラスで表現すると以下のようになります。`.`（ドット）だと何にでもマッチしてしまうので数字じゃなくてもマッチしてしまいますが、文字クラスにすれば数字だけにマッチさせられます。

```js:javascript
// YYYY/MM/DD 形式にマッチさせる。
var str1 = "2017-4-1";
var str2 = "2017/04/01";
var str3 = "yyyy/mm/dd";
console.log( /[0-9][0-9][0-9][0-9]\/[0-9][0-9]\/[0-9][0-9]/.test(str1) );  // false
console.log( /[0-9][0-9][0-9][0-9]\/[0-9][0-9]\/[0-9][0-9]/.test(str2) );  // true
console.log( /....\/..\/../.test(str3) );  // true 数字じゃなくてもマッチしてしまう
console.log( /[0-9][0-9][0-9][0-9]\/[0-9][0-9]\/[0-9][0-9]/.test(str3) );  // false
```

文字クラスを使えば、日付形式がスラッシュ区切りでもハイフン区切りでもマッチさせることができます。

```js:javascript
// YYYY/MM/DD or YYYY-MM-DD 形式にマッチさせる。
// [-\/] で日付の区切り文字にマッチさせる。
var str1 = "2017-4-1";
var str2 = "2017/04/01";
var str3 = "2017-04-01";
console.log( /[0-9][0-9][0-9][0-9][-\/][0-9][0-9][-\/][0-9][0-9]/.test(str1) );  // false 数字の文字数が合わない
console.log( /[0-9][0-9][0-9][0-9][-\/][0-9][0-9][-\/][0-9][0-9]/.test(str2) );  // true
console.log( /[0-9][0-9][0-9][0-9][-\/][0-9][0-9][-\/][0-9][0-9]/.test(str3) );  // true
```


#### 文字クラス（否定）`[^]`

文字クラスの先頭に `^`（キャレット、ハット）をつけると角括弧内の文字の否定になります。
例えば `[^>]` は `>` 以外にマッチするので `<[^>]>` とすればHTMLタグっぽいものにマッチさせられます。（１文字のタグにしかマッチさせられませんが…）

```js:javascript
var str1 = "[p]";
var str2 = "<p>";
var str3 = "<>>";
console.log( /<[^>]>/.test(str1) );  // false
console.log( /<[^>]>/.test(str2) );  // true
console.log( /<[^>]>/.test(str3) );  // false
```


### 量指定子

#### `*`（アスタリスク）

アスタリスクは直前の文字（アトム）が0回以上という意味になります。

```js:javascript
var str1 = "[]";
var str2 = "[123]";
var str3 = "[abc]";
console.log( /\[[0-9]*\]/.test(str1) );  // true 0文字でも数字の0回以上なのでマッチする
console.log( /\[[0-9]*\]/.test(str2) );  // true
console.log( /\[[0-9]*\]/.test(str3) );  // false 数字以外が0文字より多いのでマッチしない
```


#### `+`（プラス）

プラスは直前の文字（アトム）が1回以上という意味になります。
※BRE（UNIXコマンド等）では `\+` とバックスラッシュが必要です。

```js:javascript
var str1 = "[]";
var str2 = "[123]";
var str3 = "[abc]";
console.log( /\[[0-9]+\]/.test(str1) );  // false 数字の1回以上なので空文字もマッチしなくなる
console.log( /\[[0-9]+\]/.test(str2) );  // true
console.log( /\[[0-9]+\]/.test(str3) );  // false 数字以外なのでマッチしない
```


#### `?`（疑問符、クエスチョンマーク、はてなマーク、耳垂れ）

疑問符は直前の文字（アトム）が0または1回という意味になります。
あってもなくてもいいという際に使います。
※BRE（UNIXコマンド等）では `\?` とバックスラッシュが必要です。

```js:javascript
var str1 = "icon.jpg";
var str2 = "icon.jpeg";
var str3 = "icon.gif";
console.log( /icon\.jpe?g/.test(str1) );  // true
console.log( /icon\.jpe?g/.test(str2) );  // true
console.log( /icon\.jpe?g/.test(str3) );  // false
```


#### `{}`（波括弧、中括弧、ブレイス、カーリーブラケット、カール）

波括弧は直前の文字（アトム）の繰り返し数を指定したい場合に使います。
`,`（カンマ）で範囲指定も可能です。
※BRE（UNIXコマンド等）では `\{\}` とバックスラッシュが必要です。

`{n}` は n 回の繰り返し
`{n,m}` は n ～ m 回の繰り返し
`{,m}` は 0 ～ m 回の繰り返し
`{n,}` は n 回以上の繰り返し

```js:javascript
// YYYY/MM/DD or YYYY-MM-DD 形式にマッチさせる。
var str1 = "2017-4-1";
var str2 = "2017/04/01";
var str3 = "2017-04-01";
console.log( /[0-9]{4}[-\/][0-9]{1,2}[-\/][0-9]{1,2}/.test(str1) );  // true 月と日が1桁でもマッチする
console.log( /[0-9]{4}[-\/][0-9]{1,2}[-\/][0-9]{1,2}/.test(str2) );  // true
console.log( /[0-9]{4}[-\/][0-9]{1,2}[-\/][0-9]{1,2}/.test(str3) );  // true
```


### 位置指定子

#### `^`（キャレット、ハット）

キャレットは行頭にマッチします。
※言語やオプションによっては文頭や「改行の次」になります。

```js:javascript
var str1 = "www.qiita.com";
var str2 = "qiita.com";
var str3 = "qiita.www";
console.log( /^www\./.test(str1) );  // true
console.log( /^www\./.test(str2) );  // false
console.log( /^www\./.test(str3) );  // false
```

#### `$`（ドル）

ドルは行末にマッチします。
※言語やオプションによっては文末や「改行の前」になります。

```js:javascript
var str1 = "icon.jpg";
var str2 = "icon.jpeg";
var str3 = "jpg.icon";
console.log( /jpe?g$/.test(str1) );  // true
console.log( /jpe?g$/.test(str2) );  // true
console.log( /jpe?g$/.test(str3) );  // false
```


### 包括指定子と論理和指定子

#### 包括指定子 `()`（カッコ、丸括弧、パーレン）

カッコで囲ったパターンはグループ化できるだけでなく、キャプチャされて後方参照できます。
※BRE（UNIXコマンド等）では `\(\)` とバックスラッシュが必要です。

```js:javascript
var str1 = "123-456-789";
var str2 = "123-123-123";
var str3 = "abc-abc-abc";
console.log( /([0-9]{3})-\1-\1/.test(str1) );  // false 後方参照はパターン（[0-9]{3}）ではなくマッチした文字列（123）を\1で参照します。この場合は 123 ではないのでマッチしない。
console.log( /([0-9]{3})-\1-\1/.test(str2) );  // true
console.log( /([a-zA-Z]{3})-\1-\1/.test(str3) );  // true
```

カッコでキャプチャされたパターンを後方参照で使いたい場合は `\1`、`\2` などバックスラッシュと数字で表現します。カッコの数字はより左から、より外側から順に数えていきます。

後方参照による置換などは言語によって方法が異なります。

```js:javascript
var str = "abcdefg";
var rep = str.replace(/(a(b)c(d)e)f(g)/, "1:$1 2:$2 3:$3 4:$4" );
console.log( rep );  // 1:abcde 2:b 3:d 4:g
```


#### 論理和指定子 `|`（縦棒、縦線、パイプ、パイプライン、バーティカルバー、フェンス）

縦棒はいわゆる or（いずれか）という意味になります。
※BRE（UNIXコマンド等）では `\|` とバックスラッシュが必要です。

```js:javascript
// 携帯電話番号（080 or 090）にマッチさせる。PHS（050）にはマッチさせない。
var str1 = "090-1234-5678";
var str2 = "080-9876-5432";
var str3 = "050-3333-3333";
console.log( /0(8|9)0-[0-9]{4}-[0-9]{4}/.test(str1) );  // true
console.log( /0(8|9)0-[0-9]{4}-[0-9]{4}/.test(str2) );  // true
console.log( /0(8|9)0-[0-9]{4}-[0-9]{4}/.test(str3) );  // false
```


## エスケープ `\`（バックスラッシュ〔日本語環境では円記号となる場合もある〕）

`*`（アスタリスク）や `.`（ドット）といったメタ文字をエスケープした場合は、特殊な意味が無くなりその文字そのものを表すことになります。メタ文字ではない文字の前にバックスラッシュを入れても無視されます。(例えば `\j` がメタ文字でなければ、`/\j/` と `/j/` は同じものにマッチする。)


# 最後に

基本の正規表現を押さえるだけでいろんなパターンにマッチさせられます。例に挙げた日付や電話番号はもちろん、メールアドレス、URL等を表現することも可能です。

また、ここでは挙げなかったPerlの拡張された正規表現はさらに便利なメタ文字やオプションが使え、できることが広がります。CSVやHTMLのパースも可能になります。

ぜひ挑戦してみてください。