正規表現



# 正規表現の特徴
プログラミング中で正規表現を利用するケースは多くの場合以下の3つ

- 文字列の一部分を置換する
- 文字列が制約を満たしているか調べる
- 文字列の一部分を抽出する

## 文字列の一部分を置換する
最初の例で紹介した電話番号のように、ハイフンを空文字に置換することで異なるフォーマットの文字列を同一フォーマットに変換することができます。

## 文字列が制約を満たしているか調べる
ユーザ登録の際に設定するパスワードにおいて、8文字以上という制約を付けたい場合にも正規表現を用いて実装することができます。

## 文字列の一部分を抽出する
入力されたメールアドレスからドメインの部分のみを抽出したい場合などに使用します。

これらの特徴は2つのメソッドのみで全て実装することができます。このカリキュラムでは、その２つのメソッドを紹介し様々なケースで使用することで正規表現に慣れていきましょう。

# 正規表現を実現する2つのメソッド
Rubyにおいて正規表現を利用するために重要な2つのメソッドを紹介します。

subメソッド
matchメソッド

## subメソッド

まずイメージを掴みましょう

```ruby

irb(main):001:0> str = "りんごを食べる"
=> "りんごを食べる"

irb(main):002:0> str.sub(/りんご/,"みかん")
=> "みかんを食べる"
```

subメソッドは、文字列の指定した部分を別の文字列に置き換えるためのメソッドです。第一引数に置き換えたい文字列を指定し、第２引数に変換後の文字列を指定します。
また、操作したい文字列は/で囲みます。

また、JavaScriptでは、subメソッドと同じ機能を持つメソッドとして、replaceメソッドがあります。

## irbでsubメソッドを使ってみよう
まずはirbを起動しましょう。

ターミナル

```ruby

$ irb
irb(main):001:0> #ここからRubyのコードを書いていきます。
```

「おにぎりを食べる」という文字列を「ラーメンを食べる」に変換しましょう。

```ruby
irb(main):001:0> str = "おにぎりを食べる"
=> "おにぎりを食べる"

irb(main):002:0> str.sub(/おにぎり/,"ラーメン")
=> "ラーメンを食べる"
```

## matchメソッド
まずはイメージを掴みましょう。以下の例を御覧ください。

```ruby
irb(main):001:0> str = "Hello, World"
=> "Hello, World"

irb(main):002:0> str.match(/Hello/)
=> #<MatchData "Hello">

irb(main):003:0> str.match(/Good/)
=> nil
```

上の例では「Hello World」という文字列に、「Hello」という文字列が含まれているか否かをチェックしています。

### matchメソッド
matchメソッドは引数に指定した文字列がレシーバの文字列に含まれているか否かをチェックするためのメソッドです。含まれている場合は、指定した文字列がMatchDataオブジェクトの返り値で得られます。また、含まれていない場合は、返り値としてnilが得られます。

### MatchDataオブジェクト
マッチした文字列等はMatchDataオブジェクトで返されます。MatchDataオブジェクトから文字列等を取り出す際は、以下の様に配列からデータを取り出す時と同様の形で取り出すことができます。

```ruby
irb(main):001:0> str = "Hello, World"
=> "Hello, World"

irb(main):002:0> md = str.match(/Hello/)
=> #<MatchData "Hello">

irb(main):003:0> md[0]
=> "Hello"
```

irbでmatchメソッドを使ってみよう
irbを使って、「good friend」という文字列に「good」という文字列が含まれているかをチェックしてみましょう。

```ruby

irb(main):001:0> str = "good friend"
=> "good friend"

irb(main):002:0> md = str.match(/good/)
=> #<MatchData "good">

irb(main):003:0> md[0]
=> "good"
```

## 正規表現の様々なパターンを使ってみよう
subメソッド、matchメソッドの基本的な使い方を学習しました。ここからは、正規表現の様々なパターンを使いより応用的な使用方法を見ていきましょう。

今回、紹介するパターンは以下の３つです。

- 電話番号のハイフンを取り除く
- パスワードに英数字８文字以上という制約を設定する
- メールアドレスからドメインの部分のみ抽出する


## 電話番号のハイフンを取り除く
特定の文字を取り除く場合は、「特定の文字を空文字に置換する」と考えます。置換するメソッドはsubメソッドでした。以下のコードがハイフンを取り除くための処理です。しかしながら、subメソッドでは最初のハイフンしか置換されません。そこでgsubメソッドを用いることにします。

```ruby
irb(main):001:0> tel = '090-1234-5678'
=> "090-1234-5678"

irb(main):002:0> tel.sub(/-/,'')
=> "0901234-5678"
# 最初のハイフンしか置換されない

irb(main):003:0> tel.gsub(/-/,'')
=> "09012345678"
```

### 解説
ポイント
グローバルマッチのg
subの前にgが追加された、gsubメソッドが登場しました。このgが意味するのは、グローバルマッチと呼ばれ、文字列内で指定した文字が複数含まれている場合、その全てを置換するという意味になります。gsubではなくsubを使用した場合、初めの１つだけ置換されます。

## パスワードに英数字８文字以上という制約を設定する
ここまでは、ある特定の文字列の操作を行って来ました。今回の「英数字８文字以上」という制限は今までの方法とは少し別の方法で実装します。

今回はパスワードに「Hoge1234」という大文字小文字を区別した英字と数字を使用することを想定します。

matchメソッドを使用して以下のように記述します。

```ruby
irb(main):001:0> pass = 'Hoge1234'
=> "Hoge1234"

irb(main):002:0> pass.match(/[a-z\d]{8,}/i)
=> #<MatchData "Hoge1234">
```

解説
ポイント
- [a-z]: 角括弧で囲まれた文字のいずれか 1 個にマッチ
- \d: 数字にマッチ
- {n, m}: 直前の文字が少なくとも n 回、多くても m 回出現するものにマッチ
- i: 大文字・小文字を区別しない検索


### [a-z] : 角括弧で囲まれた文字のいずれか 1 個にマッチ
[](角括弧)を使用することで角括弧で囲まれた文字のいずれか１つがマッチするかをチェックしています。また、-(ハイフン)を使用することで範囲を設定することができます。[a-z]はアルファベットのa からzまでのいずれかにマッチという意味になります。

#### a~cの英字を抽出

```ruby
irb(main):001:0> 'dog'.match(/[a-c]/)
=> nil
```

「dog」という単語にはa ~ cのどの英字も含まれていないのでマッチしません。

### \d : 数字にマッチ
このdは数字を表します。数字を表すdのような文字を特殊文字と呼びます。特殊文字を使用する場合は直前に\を記述するというルールが存在します。

今回は、\dは角括弧の内部にあるので、[a-z\d]は「英数字のいずれか１つにマッチ」という意味になります。

#### 数字のみ抽出する

```ruby
irb(main):001:0> 'I have 3 pens'.match(/\d/)
=> #<MatchData "3">
```

### {n, m} : 直前の文字が少なくとも n 回、多くても m 回出現するものにマッチ
波括弧を使用することで文字数の制約を追加することができます。{8, }は、直前の文字が少なくとも８回出現するものにマッチという意味になります。今回、波括弧の直前は[a-z\d]でした。つまり、英数字のいずれか１つが少なくとも８回出現するものにマッチという意味になります。

#### 少なくとも４回、多くても６回出現するものにマッチ

```ruby
irb(main):001:0> '12345678'.match(/\d{4,6}/)
=> #<MatchData "123456">

irb(main):002:0> '123'.match(/\d{4,6}/)
=> nil
```

### i : 大文字・小文字を区別しない検索
最後にiオプションを加えることで大文字・小文字を区別せずに検索します。iオプションを付けない場合ですと、[a-z]と小文字で記述しているので大文字にマッチしなくなってしまいます。

#### 大文字・小文字の区別

```ruby
irb(main):003:0> 'Cat'.match(/cat/)
=> nil

irb(main):004:0> 'Cat'.match(/cat/i)
=> #<MatchData "Cat">
```

### 実践的な使用方法
パターンにマッチした場合はマッチした要素の配列が返されます。またマッチしなかった場合はnullが返ってくるので、その性質を使用してif文で処理を分けます。

```ruby
pass = 'Hoge1234'
if pass.match(/[a-z\d]{8,}/i)
  //パスワード設定の処理
else
   puts 'パスワードの形式が間違っています。'
end
```

### メールアドレスからドメインの部分のみ抽出する
今回は、「hoge@tech-camp.com」というアドレスから「@tech-camp.com」の部分のみを取得したいと思います。


今回もmatchメソッドを使用することで抽出することができます。

```ruby
irb(main):001:0> mail = 'hoge@tech-camp.com'
=> "hoge@tech-camp.com"

irb(main):002:0> mail.match(/@.+/)
=> #<MatchData "@tech-camp.com">
```

## 解説
ポイント

-  . :どの1 文字にもマッチ
- + :直前の文字の 1 回以上の繰り返しにマッチ

#### . どの１文字にもマッチ
ハイフンやピリオドなど含めた全ての英数字において、どの１文字にもマッチします。

```ruby
irb(main):001:0> 'hoge'.match(/./)
=> #<MatchData "h">
```

#### + 直前の文字の 1 回以上の繰り返しにマッチ
直前の文字が１回以上の繰り返しにマッチします。

```ruby
irb(main):001:0> 'aaabb'.match(/a+/)
=> #<MatchData "aaa">
```
これらを踏まえますと、.+は何かしらの文字が１回以上繰り返すものにマッチします。先頭に@を付けることで「＠から始まり、何かしらの文字が１回以上繰り返すものにマッチ」という意味になります。

こうすることでメールアドレスからドメイン部分のみを抽出することができます。

# 正規表現のパターン表記一覧

| パターン | 意味 |
|:--|:--|
| [a-z] | 角括弧で囲まれた文字のいずれか 1 個にマッチ |
| \d | 数字にマッチ |
| {n, m} | 直前の文字が少なくとも n 回、多くても m 回出現するものにマッチ |
| . | どの1 文字にもマッチ |
| + | 直前の文字の 1 回以上の繰り返しにマッチ |