連載
» 2014年06月26日 18時00分 公開

若手エンジニア/初心者のためのRuby 2.1入門(5):RubyのString/Regexpクラスによる強力な文字列操作/正規表現 (2/3)

[著:麻田優真、監修:山根剛司,株式会社アジャイルウェア]

正規表現とRegexpクラス

 正規表現は、文字列の特定のパターンに対するマッチを表現し、1つの文字列でたくさんの文字列を表現するための仕組みです。正規表現を使うことにより、以下のようなことができます。

  • ある文字列が特定のパターンを持っているか調べる
  • ある文字列から特定のパターンを持つ部分を抜き出す
  • ある文字列の特定のパターンを他の文字列で置き換える

 ここでは、Rubyで正規表現を扱うためのクラスであるRegexpクラスについて学びます。Rubyの入門記事であるという特性上、よく使われるような正規表現しか説明しませんので、不慣れな方や、より詳しく学びたい方は専門の書籍などで正規表現を学ぶことをお勧めします。

Regexpクラスのオブジェクトを生成する

スラッシュで囲む

 最も簡単な記法は、スラッシュで囲んだ正規表現リテラルを使うことです。

[1] pry(main)> /regex/
=> /regex/

 戻り値としてスラッシュで囲まれた文字列が得られました。これが正規表現オブジェクトを表す文字列です。ダブルクォートで囲んだ場合は、通常の文字列となります。

[2] pry(main)> "string"
=> "string"

Regexpクラスのnewを使う

 また、Regexpクラスのクラスメソッドであるnewを使うことで、文字列から正規表現オブジェクトを生成することも可能です。この記法は、動的に正規表現オブジェクトを生成したいときに役立ちます。

[4] pry(main)> Regexp.new("regex")
=> /regex/

特定のパターンを持っているかどうかを調べる「マッチング」

 ここでは、最も基本的な正規表現の使い方である、特定のパターンを持っているかどうかを調べる(マッチング)方法を紹介します。

=~演算子でマッチング

 マッチングのためには、=~演算子を使います。

[1] pry(main)> "Alice in Wonderland" =~ /Wonderland/
=> 9
[2] pry(main)> "Alice in Wonderland" =~ / in /
=> 5
[3] pry(main)> "Alice in Wonderland" =~ /cheshire cat/
=> nil

 =~演算子の戻り値は、マッチした部分の開始点の0から数えたインデックスとなります。[1]では、"Alice in Wonderland"の0から数えた9文字目が戻り値となっています。また、[2]のようにスペースが入っている場合は、スペースも文字として扱います。従って、[2]の戻り値は0から数えた5文字目となります。

 [3]は文字列が正規表現にマッチしない場合の例です。この場合、戻り値はnilとなります。

 マッチしない場合の戻り値がnilであることを利用して、if式の分岐の条件とするような使い方がポピュラーです。if式を利用した、1番目のコマンドライン引数に与えられた文字列が、2番目のコマンドライン引数に与えられた正規表現とマッチするかを調べる簡単なプログラムをregexp01.rbに示します。

 ここでは、コマンドライン引数に応じて動的に正規表現オブジェクトを生成する必要があるため、Regexp.new()を使っています。

if ARGV[0] =~ Regexp.new(ARGV[1])
  puts "文字列 #{ARGV[0]} は 正規表現 #{ARGV[1]} とマッチします"
else
  puts "文字列 #{ARGV[0]} は 正規表現 #{ARGV[1]} とマッチしません"
end
regexp01.rb
$ ruby regexp01.rb "Alice in Wonderland" Wonderland
文字列 Alice in Wonderland は 正規表現 Wonderland とマッチします
$ ruby regexp01.rb "Alice in Wonderland" "Cheshire cat"
文字列 Alice in Wonderland は 正規表現 Cheshire cat とマッチしません
regexp01.rbの実行結果

!~演算子はマッチしない

 また、!~演算子を用いると、文字列が正規表現にマッチしないことを調べることができます。==演算子と!=演算子の関係とよく似ていますね。

[4] pry(main)> "Alice in Wonderland" !~ /Wonderland/
=> false
[5] pry(main)> "Alice in Wonderland" !~ / in /
=> false
[6] pry(main)> "Alice in Wonderland" !~ /cheshire cat/
=> true

 [4]や[5]のように、マッチする場合にfalseが返り、マッチしない場合には[6]のようにtrueが返ります。

正規表現の真髄! Regexpクラスで使える「メタ文字」

 先ほどの例は、文字列の中から特定の文字列(Wonderlandなど)が含まれるか否かを調べるようなものでした。正規表現の真髄は、文字の繰り返しを表現するなど、特殊な用途に使えるメタ文字列の存在にあります。メタ文字列を使うことで、より多くの文字列パターンを表現できます。

 以降、Regexpクラスについて、正規表現の簡単な利用例と併せて紹介します。もっとRegexpクラスや正規表現について知りたい場合は、リファレンスマニュアルを参考にしてください。

繰り返しのメタ文字

 メタ文字の中で最もよく使われるのは、おそらく繰り返しでしょう。以下の表に繰り返しのメタ文字列の一覧を示します。

メタ文字列 意味
* 0回以上
+ 1回以上
? 0回か1回
{n} n回
{n,} n回以上
{,n} n回以下
{n,m} n回以上m回以下

String#matchメソッドでマッチング

 以下にいくつか例を示します。ここで用いているString#matchメソッドは、引数に与えられた正規表現を使って対象の文字列でマッチングを行います。もしマッチした場合は、マッチ自体を表現するMatchDataクラスのオブジェクトを返し、そうでない場合はnilを返します。

[1] pry(main)> "abbbc".match(/ab*c/)
=> #<MatchData "abbbc">
[2] pry(main)> "ac".match(/ab*c/)
=> #<MatchData "ac">
[3] pry(main)> "ac".match(/ab+c/)
=> nil
[4] pry(main)> "ac".match(/ab{1,2}c/)
=> nil
[5] pry(main)> "abc".match(/ab{1,2}c/)
=> #<MatchData "abc">
[6] pry(main)> "abbc".match(/ab{1,2}c/)
=> #<MatchData "abbc">
[7] pry(main)> "abbbc".match(/ab{1,2}c/)
=> nil

 [1]と[2]は、aから始まり、bが0個以上続き、cで終わる部分文字列にマッチする正規表現の例です。0個以上の繰り返しですので、この正規表現は[2]のように"ac"というbを含まない文字列に対してもマッチします。[3]はbの1個以上の繰り返しを指定しているので、"ac"という文字列にはマッチしません。

 [4]〜[7]は中括弧を使った繰り返し用のメタ文字列の例です。ここでは、aから始まり、bが1個以上2個以下続き、cで終わる部分文字列にマッチする正規表現を使っています。ですので、[4]や[7]のような"ac"、"abbbc"にはマッチせず、[5]と[6]のような"abc"、"abbc"といった文字列にマッチします。

改行以外の任意の1文字にマッチするメタ文字「.」(ドット)

 また、繰り返しと同時によく使われるメタ文字に、.(ドット)があります。これは、改行以外の任意の1文字にマッチするメタ文字です。

[8] pry(main)> "abc".match(/a.?c/)
=> #<MatchData "abc">
[9] pry(main)> "a0c".match(/a.?c/)
=> #<MatchData "a0c">
[10] pry(main)> "a@c".match(/a.?c/)
=> #<MatchData "a@c">
[11] pry(main)> "axxc".match(/a.?c/)
=> nil

 [8]から[11]の正規表現は、aから始まり、0文字または1文字の任意の文字が続き、cで終わる部分文字列にマッチします。従って、[8]〜[10]のような文字列にはマッチしますが、xが2個続いている[11]はマッチしません。

いくつかの文字の中から、そのうちの1文字にマッチさせる「文字クラス」

  • 角カッコを用いる基本的な文字クラス

 いくつかの文字の中から、そのうちの1文字にマッチさせたい場合があります。その場合、角カッコを用いた「文字クラス」という表現が便利です。文字クラスでは角カッコに列挙した文字の中から、1文字を選んでマッチさせることができます。以下に文字クラスの例を示します。

[1] pry(main)> "Alice".match(/[A4a]lice/)
=> #<MatchData "Alice">
[2] pry(main)> "4lice".match(/[A4a]lice/)
=> #<MatchData "4lice">
[3] pry(main)> "alice".match(/[A4a]lice/)
=> #<MatchData "alice">
[4] pry(main)> "Ilice".match(/[A4a]lice/)
=> nil

 [1]〜[4]で用いた正規表現は、Aまたは4またはaから始まり、liceと続く文字列にマッチします。従って、[1]〜[3]の例ではマッチしますが、[4]の例ではAでも4でもaでもない、Iという文字から始まっているのでマッチしません。

  • 否定文字クラス「^」(ハット)

 また、「否定文字クラス」と呼ばれる、列挙されていない文字とマッチさせる方法もあります。否定文字クラスは角カッコの開始直後に、^(ハット)を挿入することで表現できます。

 以下は否定文字クラスの例です。

[5] pry(main)> "Alice".match(/[^A4a]lice/)
=> nil
[6] pry(main)> "Ilice".match(/[^A4a]lice/)
=> #<MatchData "Ilice">

 [5]と[6]の例から、[1]〜[4]の例に対してマッチ結果が反転していることが分かります。

  • 範囲指定を用いた文字クラス

 aからzまでのアルファベットを列挙する場合などに使える文字クラスの記法もあります。以下はその例です。

[7] pry(main)> "alice".match(/[a-z]lice/)
=> #<MatchData "alice">
[8] pry(main)> "Alice".match(/[a-z]lice/)
=> nil
[9] pry(main)> "alice".match(/[A-Z]lice/)
=> nil
[10] pry(main)> "Alice".match(/[A-Z]lice/)
=> #<MatchData "Alice">
[11] pry(main)> "42".match(/[0-9]2/)
=> #<MatchData "42">
[12] pry(main)> "A2".match(/[0-9]2/)
=> nil

 [7]および[8]の正規表現は、aからz、つまり小文字のアルファベットにマッチします。[7]は一文字目が小文字なのでマッチし、[8]は一文字目が大文字なのでマッチしません。

 [9]および[10]の正規表現は、AからZ、つまり大文字のアルファベットにマッチします。[9]は一文字目が小文字なのでマッチせず、[10]は一文字目が大文字なのでマッチします。

 [11]および[12]の正規表現は、0から9、つまり一桁の数字にマッチします。[11]は一文字目が数字なのでマッチし、[12]は一文字目がアルファベットなのでマッチしません。

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。