【Ruby】正規表現の概要

#はじめに
学習中の備忘録です。

#概要
使用頻度の高いパターンについてまとめ

* 正規表現を実現する2つのメソッド
* 文字列の一部分を置換する
* 文字列が制約を満たしているか調べる
* 文字列の一部分を抽出する
* 正規表現のパターン表記一覧

#前提
Ruby 2.5.1
ターミナルにてirbコマンドで処理結果を確認

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

```ruby:ターミナル
irb(main):001:0> str = "りんごを食べる"
=> "りんごを食べる"

irb(main):002:0> str.sub(/りんご/,"みかん")
=> "みかんを食べる"
```
###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"
```

#文字列の一部分を置換する
###電話番号からハイフンを取り除く
特定の文字を取り除く場合は、「特定の文字を空文字に置換する」と考えます。置換するメソッドは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@abcd.com」というアドレスから「@abcd.com」の部分のみを取得したいと思います。
今回もmatchメソッドを使用することで抽出することができます。

```ruby:ターミナル
rb(main):001:0> mail = 'hoge@abcd.com'
=> "hoge@abcd.com"

irb(main):002:0> mail.match(/@.+/)
=> #<MatchData "@abcd.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 回以上の繰り返しにマッチ|

#まとめ
正規表現について新たな学習があれば適宜追記するかもです。