【Ruby】match()メソッドで文字列を検索/抽出する方法を徹底解説!

  • 2021年8月11日
  • 2021年12月19日
  • Ruby

Rubyで文字列処理を実装するうえで便利なのが、正規表現と呼ばれる文字列の表現方法です。正規表現は、元の文字列から該当する部分的な文字列を検索するのに用いられ、match()メソッドと呼ばれる文字列検索メソッドと併用することが多いです。

今回は、正規表現を使った文字列検索処理を実装するのにうってつけな、match()メソッドの使い方を詳しく解説。類似した機能を持つメソッドとの違いについても、ソースコードを交えて解説しています。文字列検索処理について理解したい方は、ぜひ参考にしてください。

なお、Rubyの魅力や特徴については以下の記事で詳しく解説していますので、こちらもぜひご覧くださいね。

おすすめ記事

みなさんこんにちは! 今回の記事は、 悩みを抱えた人 ・Rubyってどんなプログラミング言語なの?・Rubyを勉強すると将来役に立つかな?・Rubyのおすすめの学習方法が知りたい! というお悩みを解決する記事にな[…]

Rubyとは?その特徴や将来性、おすすめの学習方法まで網羅的にご紹介します

フリーランス案件を探すならこのエージェント!

運営会社 レバテック株式会社 株式会社Branding Engineer 株式会社Hajimari
対応エリア 東京・神奈川・埼玉・千葉
大阪・兵庫・京都・愛知・福岡
東京・神奈川・埼玉・千葉
大阪・兵庫・京都・奈良・和歌山・滋賀
東京・神奈川・埼玉・千葉
茨城・大阪・兵庫・福岡
案件数 約28,000件 約3,300件 約4,200件
平均単価 68.2万円 72.2万円
最高月収 145万円 200万円 176万円
支払いサイト 15日(月末締め翌月15日支払い) 20日
手数料(中間マージン) 非公開 非公開
※実質20%
非公開
特徴高単価な案件が多い
福利厚生サービスが受けられる
・首都圏の案件が中心
給与保証制度で安定した収入を得られる
・税務処理のサポートあり
・マージン率は実質20%
週2/週3の案件が多い
在宅/リモート案件が多い
・スタートアップ企業やベンチャー企業の案件が多い
紹介企業例 ・株式会社Gunosy
・株式会社バンダイナムコホールディングス
・株式会社ユーザベース
・Sansan株式会社
・株式会社一休
・株式会社FiNC Technologies など
・クックパッド株式会社
・株式会社カオナビ
・株式会社Kaizen Platform など
説明記事
公式サイト


転職を考えているならこのエージェント!

運営会社 レバテック株式会社 株式会社Branding Engineer 株式会社リブセンス
対応エリア 東京・神奈川・埼玉・千葉
大阪・兵庫・京都・愛知・福岡
東京・神奈川・埼玉・千葉
大阪・兵庫・京都・奈良・和歌山・滋賀
東京・神奈川・埼玉・千葉
茨城・大阪・兵庫・福岡
案件数 約13,000件 約800件
※非公開求人が多い

※ドラフト制のため、対象外
年齢層 20代:◎
30代:〇
40代:〇
20代:〇
30代:〇
40代:△
20代:〇
30代:〇
40代:〇
特徴エンジニア・デザイナーを専門としている
・カウンセリングに時間をかける
年収アップ率60%の実績
IT/Web/ゲーム業界専門の転職エージェント
・業界経験者がキャリアコンサルタントを務める
・丁寧なカウンセリングで入社後の離職率1%以下を実現
ドラフト制による採用を叶える特殊なサービス
スキル・経験重視で評価してもらえる
・他エンジニアの入札結果を見ることで
自分の市場価値が分かる
紹介企業例 ・サイボウズ株式会社
・株式会社サイバーエージェント
・Chatwork株式会社 など
・DMM.com
・Money Forward
・SmartHR など
・メルカリ
・ZOZOテクノロジーズ
・freee など
説明記事
公式サイト



match()メソッドの基本

match()メソッドは、RubyのStringクラスとRegExpクラスで用意されているインスタンスメソッドです。

Stringクラスのmatch()メソッド

Stringクラスで用意されているmatch()メソッドは、文字列オブジェクトをレシーバとし、正規表現を引数とすることで文字列の検索処理を実行します。文字列がヒットした場合は、ヒットした文字列や関連情報を保有するオブジェクトが返され、逆にヒットする文字列がない場合は、nilが返されます。match()メソッドのサンプルコードを以下に示します。

sample_str_1 = "This is a pen. Do you use this pen?"
sample_str_2 = "This is an apple. Do you eat this apple?"

# 文字列をレシーバとしてmatch()メソッドを呼び出す
sample_match_data_1 = sample_str_1.match(/pen/)
sample_match_data_2 = sample_str_2.match(/pen/)

p sample_match_data_1 # #<MatchData "pen">
p sample_match_data_2 # nil

2つの文字列を対象として、文字列中に「pen」が存在するかどうかをmatch()メソッドで検索するプログラムを実装しています。今回は「pen」を検索したいので、対象の単語をスラッシュで囲んだ正規表現「/pen/」を引数として渡します。

単語がヒットした場合、戻り値はMatchDataと呼ばれるオブジェクトです。一方、単語がヒットしなかった場合は、nilが返されます。戻り値の違いによって、マッチする文字列が含まれているか否かを判断することができます。

文字列がヒットした場合に返されるMatchDataオブジェクトは、正規表現でグループを定義した個数によってサイズが変動します。以下のサンプルコードをご覧ください。

sample_product_key = "0E3R-1W2F-4H6T"

# 丸括弧でグループ化した正規表現でmatch()メソッドを呼び出す
sample_match_data_3 = sample_product_key.match(/(\w+)-(\w+)-(\w+)/)

# ヒットした文字列がグループごとに格納されている
p sample_match_data_3 # #<MatchData "0E3R-1W2F-4H6T" 1:"0E3R" 2:"1W2F" 3:"4H6T">

# captures()メソッドでヒットした文字列の配列を生成できる
p sample_match_data_3.captures # ["0E3R", "1W2F", "4H6T"]

プロダクトキーから英数字の部分をグループごとに抜き出すプログラムです。正規表現で丸括弧を用いることでグループを定義できます(4行目)。グループが定義された正規表現でmatc()メソッドを呼び出した場合、グループの個数分MatchDataオブジェクトにヒットした文字列が格納されます。

ヒットした文字列に対して何らかの処理を施したい場合は、captures()メソッドで配列を生成するのがおすすめです。

RegExpクラスのmatch()メソッド

RegExpクラスで用意されているmatch()メソッドは、Stringクラスのものとは違い、正規表現オブジェクトをレシーバとして、検索対象の文字列オブジェクトを引数にとります。戻り値は、ヒットした場合、しなかった場合ともにStringクラスのものと同様です。サンプルコードは以下の通りです。

sample_str_1 = "This is a pen. Do you use this pen?"
sample_str_2 = "This is an apple. Do you eat this apple?"

# 正規表現をレシーバとしてmatch()メソッドを呼び出す
sample_match_data_1 = /pen/.match(sample_str_1)
sample_match_data_2 = /pen/.match(sample_str_2)

# 戻り値はStringクラスのmetch()メソッドの場合と同様
p sample_match_data_1 # #<MatchData "pen">
p sample_match_data_2 # nil

サンプルコードを実行しても分かるように、レシーバと引数を入れ替えたとしても、実行結果に違いはありません。

match()メソッドの応用テクニック

ここからは、match()メソッドを利用した応用テクニックを紹介しましょう。

検索開始位置を設定する

match()メソッドによる文字列の検索は通常、文字列の先頭から実行されます。この実行開始する文字列の位置を、match()メソッドの第二引数で設定可能です。以下のサンプルコードをご覧ください。

sample_text = "0123 abcdefg 4567 hijklmn"
pattern = /(\d+)/

# 先頭から検索
sample_match_data_1 = pattern.match(sample_text)

# 2番目から検索
sample_match_data_2 = pattern.match(sample_text, 2)

# 4番目から検索
sample_match_data_3 = pattern.match(sample_text, 4)

p sample_match_data_1.captures # ["0123"]
p sample_match_data_2.captures # ["23"]
p sample_match_data_3.captures # ["4567"]

サンプル文字列の中から、数字が連続する部分のみを抽出するプログラムです。第二引数を指定しない場合は、通常通り先頭から検索を開始するため、「0123」がヒットします。次に、第二引数に2を設定した場合です。Rubyにおいては、0から数える決まりとなっているので、3文字目の「2」から検索を開始するように動作します。したがって、「23」がヒットします。同様に、第二引数に4を設定した場合、戻り値は「4567」です。

ヒットした文字列を加工する

match()メソッドにブロックを渡すことで、ヒットした文字列に対し何らかの処理を実施することができます。以下のサンプルコードをご覧ください。

sample_text_1 = "This product price is 1800 yen."

# 文字列がヒットしたらブロック内の処理が実行される
tax_included_price = sample_text_1.match(/\d+ yen/) { |match|
  p "Price excluding tax: #{match}" # "Price excluding tax: 1800 yen"

  price = match.to_a[0].to_i
  price = (price * 1.1).floor
}

p "Price including tax: #{tax_included_price} yen" # "Price including tax: 1980 yen"

sample_text_2 = "Do you buy the product?"

# 文字列がヒットしないならブロック内の処理は実行されない
tax_included_price = sample_text_2.match(/\d+ yen/) { |match|
  p "Price excluding tax: #{match}"

  price = match.to_a[0].to_i
  price = (price * 1.1).floor
}

p tax_included_price # nil

価格が含まれる文字列から価格部分のみ抜き出すプログラムです。また、match()メソッドに渡しているブロックには、抜き出した価格から税込価格を計算するプログラムが定義されています。

文字列がヒットした場合は、ブロック内の処理が実行されます。ブロック引数はヒットした文字列です。つまり、サンプルコード4行目の引数matchに「1800 yen」が格納されています。ブロック内の処理が終了すると、最終的にブロックの最後で評価された値がmatch()メソッドの戻り値として返されます。つまり、サンプルコード4行目のmatch()メソッド呼び出し時は、戻り値は変数priceの値の中身です。

一方、文字列がヒットしない場合はブロック内の処理が一切実行されません。サンプルコード16行目におけるmatch()メソッド呼び出しでは、レシーバの文章中に価格に関わる部分が含まれていないため、ブロック内の処理は実行されずnilが返されます。

match?()メソッドとの違い

match()メソッドと似た機能を持つメソッドとして、match?()メソッドがあります。match?()メソッドは、RegExpクラスにて用意されているインスタンスメソッドの1つです。正規表現オブジェクトをレシーバとし、文字列を引数として検索処理を実行する点では、match()メソッドと同様の機能を持ちます。

一方、メソッドの戻り値は両者で挙動が異なります。match()メソッドの戻り値は、文字列がヒットしたときはMatchDataオブジェクト、ヒットしなかったときはnilを返すのに対し、match?()メソッドは、検索結果に対応する論理値(trueまたはfalse)を返します。以下のサンプルコードで違いを確認してみましょう。

sample_text = "Ruby is a programming language born in Japan."
pattern = /Ruby/

# match()メソッドで文字列検索
result_1 = pattern.match(sample_text)
result_2 = pattern.match(sample_text, 1)

# match?()メソッドで文字列検索
result_3 = pattern.match?(sample_text)
result_4 = pattern.match?(sample_text, 1)

p result_1 # #<MatchData "Ruby">
p result_2 # nil
p result_3 # true
p result_4 # false

文章中に「Ruby」が含まれているかを検索する処理を、match()メソッドとmatch?()メソッドをそれぞれ利用して実装しています。サンプルコードを見ても分かるように、match?()メソッドで文字列検索をしヒットした場合は、論理値trueを返します。一方、ヒットしなかった場合はnilではなくfalseを返します。単に文字列が存在するか否かを判定するのが目的であれば、match?()メソッドを呼び出すことで、論理値による判断ができます。

scan()メソッドとの違い

もう一つ、似た機能を有するscan()メソッドについても紹介しましょう。scan()メソッドは、Stringクラスのインスタンスメソッドの1つで、正規表現オブジェクトを基にして文字列検索処理を実行します。この点については、match()メソッドと変わりありません。しかし、scan()メソッドの戻り値は配列であることが、match()メソッドと大きく異なります。以下のサンプルコードで、実行結果の違いを見てみましょう。

sample_text = "apache apart application apollo"
pattern_1 = /ap[a-z]+/
pattern_2 = /ab[a-z]+/

# match()メソッドで文字列検索
result_1 = sample_text.match(pattern_1)
result_2 = sample_text.match(pattern_2)

# scan()メソッドで文字列検索
result_3 = sample_text.scan(pattern_1)
result_4 = sample_text.scan(pattern_2)

p result_1 # #<MatchData "apache">
p result_2 # nil
p result_3 # ["apache", "apart", "application", "apollo"]
p result_4 # []

サンプルコードの変数sample_textには、apから始まる単語が4つ含まれています。文字列がヒットした場合、match()メソッドは最初にヒットした文字列が格納された、MatchDataオブジェクトが返されます。つまり、サンプルコードの変数result_1には、「apache」のみが格納されます。一方、scan()メソッドの戻り値は、ヒットするすべての文字列が格納された配列です。したがって、要素数4の配列が格納されます。

文字列がヒットしない場合も両者で戻り値が異なり、match()メソッドはnilが返されるのに対し、scan()メソッドは空の配列が返されます。scan()メソッドを利用する場合は、配列の要素数が0であるか否かで、文字列が含まれているか否かの判断が可能です。

お仕事の途中ですが、少し一休みして、転職独立について考えてみませんか🙌?

現役エンジニアが選ぶおすすめの転職エージェント11選【成功談・失敗談もあります】

レバテックフリーランスの評判ってどう?【現役エンジニアが徹底解説します】

MidWorks(ミッドワークス)の評判ってどう?【現役エンジニアが徹底解説します】

日々の業務に追われて自分を見失わないよう、
定期的にキャリアを振り返るようにしておきましょう🤲

最後に

さて、ここまでRubyのRangeクラスの取り扱い方について解説してきましたがいかがでしたか?

今回ご紹介した通り、Rangeクラスには便利なメソッドが多数準備されており、これらを使いこなすことで、コードや処理の簡素化/効率化が図れます。

実際の開発でも頻繁に登場するものばかりですので、ぜひこの機会にマスターしておいてくださいね。

このブログを通じて少しでも「傍(はた)を楽(らく)にする」ことができていれば嬉しく思います。

最後まで読んで頂きありがとうございました。