Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

知っておくと便利!Ruby正規表現のクールなトリック集

昨日は正規表現の条件分岐について取り上げましたが、今日はその流れで、Rubyの正規表現で実現できるちょっとしたクールなテクニックをいくつかご紹介します。

正規表現で文字列を分割する

文字列を特定の区切り文字で分割するのは、おそらく誰もがよく使う操作でしょう。

"one,two".split(",")
# => ["one", "two"]

しかし、splitには正規表現を渡すこともできるのです。

# `,` と `-` を区切り文字として使用
"one,two-three".split(/,|-/)
# => ["one", "two", "three"]

# 米ドルはカンマを桁区切りに、
# ユーロはスペースを使うケースの例
"1,000USD".split /(?=.*(USD))(?(1),| )/

区切り文字をキャプチャする

ここでちょっとした「パーティー芸」をご紹介しましょう。通常、文字列をsplitすると、区切り文字は消えてなくなります。

# カンマは消えてしまう!
"one,two".split(",")
# => ["one", "two"]

ところが、正規表現を使い、区切り文字をグループ () の中に入れると、splitは区切り文字も結果の配列に含めてくれるのです。

"one,two-three".split(/(,|-)/)
# => ["one", ",", "two", "-", "three"]

この挙動が起こる理由は、splitが実はキャプチャグループの境界位置でも文字列を分割しているからです。

splitの「乱用」テクニック

splitを工夫して使うと、ほぼmatchのような振る舞いをさせることもできます。以下のコードでは、正規表現内に4つのグループを用意し、文字列を4つの部分に分割しています。

"1-800-555-1212".split(/(1)-(\d{3})-(\d{3})-(\d{4})/)
# => ["", "1", "800", "555", "1212"]

結果の配列の先頭が空文字列になっているのは、正規表現が元の文字列全体にマッチしているためです。

グローバルマッチング(すべてのマッチを取得)

デフォルトでは、正規表現はパターンに一度しかマッチしません。以下のコードでは、マッチ候補が5つあるにもかかわらず、1つしか取得できません。

"12345".match /\d/
# => #<MatchData "1">

Perlなどの他の言語では、正規表現に「グローバル」フラグを付けることで解決します。Rubyにはそのオプションはありませんが、代わりにString#scanメソッドが用意されています。

scanメソッドは、すべてのマッチを含む配列を返します。

"12345".scan /\d/
# => ["1", "2", "3", "4", "5"]

さらに、便利なブロック構文も用意されています。

"12345".scan /\d/ do |i|
  puts i
end

ただし残念ながら、文字列を遅延評価(lazy)でスキャンする方法はないようです。そのため、たとえば500MBもの巨大なファイルを処理するような用途には、この手法は向いていないでしょう。

グループを使ったスキャン

ここまで読んで、「scanでグループを使うと、どんな変わったトリックができるんだろう?」と気になった方もいるかもしれません。

結論から言うと、ここの挙動は完全に予測可能で、少々退屈です。グループを使うと、結果は多次元配列になります。

"hiho hiho".scan /(hi)(ho)/
# => [["hi", "ho"], ["hi", "ho"]]

ただし、ひとつ奇妙なエッジケースがあります。グループを使用した場合、グループ外の部分は結果に含まれません。

"hiho hiho".scan /(hi)ho/
# => [["hi"], ["hi"]]

省略記法(ショートハンド)

正規表現が文字列にマッチするかどうかを調べる方法として、=~演算子をご存じの方は多いでしょう。これはマッチが始まる文字位置のインデックスを返します。

"hiho" =~ /hi/
# => 0
"hiho" =~ /ho/
# => 2

実は、マッチをチェックするもうひとつの手軽な方法があります。それが===演算子です。

/hi/ === "hiho"
# => true

Rubyでa === bと書くとき、私たちは「bはaが定義する集合に属するか?」と尋ねていることになります。この場合なら「'hiho'は正規表現/hi/がマッチする文字列の集合に属するか?」という意味です。

===演算子は、Rubyのcase文の内部でも使われています。つまり、case文の中でも正規表現を条件として使えるのです。

case "hiho"
when /hi/
  puts "match"
end

三重等号演算子(===)は、自分のメソッドが正規表現と文字列のどちらも引数として受け取れるようにしたい場合にも役立ちます。

たとえば、「エラー発生時に何らかの処理を実行したいが、あらかじめ設定した無視リストにあるクラスは除外したい」というケースを考えてみましょう。以下の例では、===演算子を使うことで、ユーザーが文字列と正規表現のどちらでも指定できるようになっています。

def ignored?(error)
  @ignored_patterns.any? { |x| x === error.class.name }
end

まとめ

もちろん、こうした小技はRubyやRailsのフレームワーク全体に数多く散りばめられています。特にお気に入りのテクニックがあれば、ぜひ教えてください!

  1. Excelでセルを分割する5つの簡単な方法【初心者向け完全ガイド】

    Microsoft Excelでは、セルを複数に分割するためのさまざまなテクニックが用意されています。数式や標準ツール、さらにはPower Queryを使って、セルや列を2つ以上に分けることが可能です。この記事では、わかりやすい手順とともに、セルやテキストを分割するための複数の方法をご紹介します。 この記事で使用しているサンプルのExcelブックもダウンロードできますので、実際に操作しながら学習できます。 Excelでセルを分割する5つの簡単な方法 1. 「区切り位置」機能を使ってセルを分割する 以下の表には、「テキスト」列に氏名データが入力されています。ここでは、各氏名を「名」と「姓」

  2. Notepad++を使いこなす!知っておきたい5つの便利テクニック

    Notepad++は、Windows向けの最高のテキストエディタと言っても過言ではありません。その機能は非常に豊富で、すべてを紹介するには何本もの記事が必要なほどです。そこで本記事では、この多機能かつ洗練されたテキストエディタの驚異的なパワーを順番にご紹介していきます。もうテキストファイルやスクリプト、HTMLファイルの編集にメモ帳やワードパッドを使う必要はありません。それでは、Notepad++を最大限に活用するための最初の5つのテクニックを見ていきましょう。1. コードに合わせて言語を設定するNotepad++の強力な機能のひとつが、コードの色分け(シンタックスハイライト)です。C言語、M