正規表現の特徴
プログラミング中で正規表現を利用するケースは多くの場合以下の3つ
- 文字列の一部分を置換する
- 文字列が制約を満たしているか調べる
- 文字列の一部分を抽出する
文字列の一部分を置換する
最初の例で紹介した電話番号のように、ハイフンを空文字に置換することで異なるフォーマットの文字列を同一フォーマットに変換することができます。
文字列が制約を満たしているか調べる
ユーザ登録の際に設定するパスワードにおいて、8文字以上という制約を付けたい場合にも正規表現を用いて実装することができます。
文字列の一部分を抽出する
入力されたメールアドレスからドメインの部分のみを抽出したい場合などに使用します。
これらの特徴は2つのメソッドのみで全て実装することができます。このカリキュラムでは、その2つのメソッドを紹介し様々なケースで使用することで正規表現に慣れていきましょう。
正規表現を実現する2つのメソッド
Rubyにおいて正規表現を利用するために重要な2つのメソッドを紹介します。
subメソッド
matchメソッド
subメソッド
まずイメージを掴みましょう
irb(main):001:0> str = "りんごを食べる"
=> "りんごを食べる"
irb(main):002:0> str.sub(/りんご/,"みかん")
=> "みかんを食べる"
subメソッドは、文字列の指定した部分を別の文字列に置き換えるためのメソッドです。第一引数に置き換えたい文字列を指定し、第2引数に変換後の文字列を指定します。
また、操作したい文字列は/で囲みます。
また、JavaScriptでは、subメソッドと同じ機能を持つメソッドとして、replaceメソッドがあります。
irbでsubメソッドを使ってみよう
まずはirbを起動しましょう。
ターミナル
$ irb
irb(main):001:0> #ここからRubyのコードを書いていきます。
「おにぎりを食べる」という文字列を「ラーメンを食べる」に変換しましょう。
irb(main):001:0> str = "おにぎりを食べる"
=> "おにぎりを食べる"
irb(main):002:0> str.sub(/おにぎり/,"ラーメン")
=> "ラーメンを食べる"
matchメソッド
まずはイメージを掴みましょう。以下の例を御覧ください。
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オブジェクトから文字列等を取り出す際は、以下の様に配列からデータを取り出す時と同様の形で取り出すことができます。
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」という文字列が含まれているかをチェックしてみましょう。
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メソッドの基本的な使い方を学習しました。ここからは、正規表現の様々なパターンを使いより応用的な使用方法を見ていきましょう。
今回、紹介するパターンは以下の3つです。
- 電話番号のハイフンを取り除く
- パスワードに英数字8文字以上という制約を設定する
- メールアドレスからドメインの部分のみ抽出する
電話番号のハイフンを取り除く
特定の文字を取り除く場合は、「特定の文字を空文字に置換する」と考えます。置換するメソッドはsubメソッドでした。以下のコードがハイフンを取り除くための処理です。しかしながら、subメソッドでは最初のハイフンしか置換されません。そこでgsubメソッドを用いることにします。
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を使用した場合、初めの1つだけ置換されます。
パスワードに英数字8文字以上という制約を設定する
ここまでは、ある特定の文字列の操作を行って来ました。今回の「英数字8文字以上」という制限は今までの方法とは少し別の方法で実装します。
今回はパスワードに「Hoge1234」という大文字小文字を区別した英字と数字を使用することを想定します。
matchメソッドを使用して以下のように記述します。
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 個にマッチ
を使用することで角括弧で囲まれた文字のいずれか1つがマッチするかをチェックしています。また、-(ハイフン)を使用することで範囲を設定することができます。[a-z]はアルファベットのa からzまでのいずれかにマッチという意味になります。
a~cの英字を抽出
irb(main):001:0> 'dog'.match(/[a-c]/)
=> nil
「dog」という単語にはa ~ cのどの英字も含まれていないのでマッチしません。
\d : 数字にマッチ
このdは数字を表します。数字を表すdのような文字を特殊文字と呼びます。特殊文字を使用する場合は直前に\を記述するというルールが存在します。
今回は、\dは角括弧の内部にあるので、[a-z\d]は「英数字のいずれか1つにマッチ」という意味になります。
数字のみ抽出する
irb(main):001:0> 'I have 3 pens'.match(/\d/)
=> #<MatchData "3">
{n, m} : 直前の文字が少なくとも n 回、多くても m 回出現するものにマッチ
波括弧を使用することで文字数の制約を追加することができます。{8, }は、直前の文字が少なくとも8回出現するものにマッチという意味になります。今回、波括弧の直前は[a-z\d]でした。つまり、英数字のいずれか1つが少なくとも8回出現するものにマッチという意味になります。
少なくとも4回、多くても6回出現するものにマッチ
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]と小文字で記述しているので大文字にマッチしなくなってしまいます。
大文字・小文字の区別
irb(main):003:0> 'Cat'.match(/cat/)
=> nil
irb(main):004:0> 'Cat'.match(/cat/i)
=> #<MatchData "Cat">
実践的な使用方法
パターンにマッチした場合はマッチした要素の配列が返されます。またマッチしなかった場合はnullが返ってくるので、その性質を使用してif文で処理を分けます。
pass = 'Hoge1234'
if pass.match(/[a-z\d]{8,}/i)
//パスワード設定の処理
else
puts 'パスワードの形式が間違っています。'
end
メールアドレスからドメインの部分のみ抽出する
今回は、「hoge@tech-camp.com」というアドレスから「@tech-camp.com」の部分のみを取得したいと思います。
今回もmatchメソッドを使用することで抽出することができます。
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 回以上の繰り返しにマッチ
. どの1文字にもマッチ
ハイフンやピリオドなど含めた全ての英数字において、どの1文字にもマッチします。
irb(main):001:0> 'hoge'.match(/./)
=> #<MatchData "h">
+ 直前の文字の 1 回以上の繰り返しにマッチ
直前の文字が1回以上の繰り返しにマッチします。
irb(main):001:0> 'aaabb'.match(/a+/)
=> #<MatchData "aaa">
これらを踏まえますと、.+は何かしらの文字が1回以上繰り返すものにマッチします。先頭に@を付けることで「@から始まり、何かしらの文字が1回以上繰り返すものにマッチ」という意味になります。
こうすることでメールアドレスからドメイン部分のみを抽出することができます。
正規表現のパターン表記一覧
パターン | 意味 |
---|---|
[a-z] | 角括弧で囲まれた文字のいずれか 1 個にマッチ |
\d | 数字にマッチ |
{n, m} | 直前の文字が少なくとも n 回、多くても m 回出現するものにマッチ |
. | どの1 文字にもマッチ |
+ | 直前の文字の 1 回以上の繰り返しにマッチ |