圧倒的備忘録

忘却は罪である。

【Ruby入門】猿でもわかる正規表現

正規表現とは

ある一定の規則で並んでいる文字列を検出する技術です。

Ruby正規表現

Ruby正規表現は二つの「/」で文字列を囲みます。例えば、「ミッキーマウス」、「ミニーマウス」、「ドナルドダック」という文字列が含まれるオブジェクトから「ミッキーマウス」と「ミニーマウス」を検出するにはどのようにすればよいでしょうか。以下は、「ミッキーマウス」と「ミニーマウス」を検出する正規表現の一例になります。

# ミッキーマウス、ミニーマウス、ドナルドダックという文字列が含まれる変数
characters = "ミッキーマウス,ミニーマウス,ドナルドダック"

# 上記変数から「マウス」という文字列が含まれるデータを全て検出します。
result = characters.match(/.*マウス/)

# 検出したデータを出力します。
# =>「ミッキーマウス」、「ミニーマウス」が表示されます。
puts result

Ruby正規表現はメタ文字とパターンマッチメソッドを組み合わせることにより、膨大なデータから特定の文字列を検出することができます。上記のプログラムでは「.」がメタ文字で、「match」がパターンマッチメソッドです。Ruby正規表現でよく使われるメタ文字は次になります。

メタ文字

メタ文字とは、文字本来の意味とは異なり、プログラムで特別な意味を持たせた文字のことです。

メタ文字 メタ文字の説明
. 1文字を指定します。(改行は除きます。)
[] 文字の集まりを指定します。[a-f]はaからfのいずれかにマッチするという意味です。
[^] 文字の集まりを逆指定します。[a-f]はaからfのどれにまマッチしないという意味です。
\w 英数字1文字を指定します。\wは大文字と小文字を区別しないため、\w[a-z]と\w[A-Z]は同じ結果になります。
\s 空白文字1文字を指定します。(空白、タブ、改行を含みます。)
\d 数字1文字を指定します。(\d\dは二桁の数字を意味します。)
* 直前のパターンを0回以上繰り返します。
+ 直前のパターンを1回以上繰り返します。
? 直前のパターンを0回または、1回繰り返します。
^ 行頭を指定します。
$ 行末を指定します。
どちらか一方が含まれている場合に処理を行います。
() グループ化をします。

これらのメタ文字にパターンマッチメソッドを組み合わせることにより、特定の文字列を検出することができます。Ruby正規表現でよく使われるパターンマッチは次になります。

パターンマッチメソッド

  • match
  • pre_match
  • post_match
  • slice

matchメソッド

正規表現にマッチした文字列を取得する時に使用します。オブジェクト名.match(/正規表現/)という記法になります。

# もし、「aabbcc」内に「abbc」が含まれているならば
if "aabbcc" =~ /ab+c/ then
 #「マッチしました!」と表示する。
    puts "マッチしました!"
 # 含まれていないならば
else
 # 「マッチしませんでした!」と表示する。
    puts "マッチしませんでした!"
end

留意点:/ab+c/内の「+」は直前のbを一回繰り返すという意味ですので、「abbc」となります。

pre_match

正規表現にマッチした前の文字列を取得する時に使います。

message = /の勉強/.match("Rubyの勉強をしよう!")
# =>「Ruby」と表示されます。
puts message.pre_match

post_match

正規表現にマッチした後の文字列を取り出す時に使用します。

message = /の勉強/.match("Rubyの勉強をしよう"!)
# =>「の勉強をしよう!」と表示されます。
puts message.pre_match

slice

正規表現にマッチした部分の文字列を特定して、一文字づつ抜き出す時に使います。

message = "hello"
# =>「hello」の左から2番目から3文字分を取得して、「ell」を出力します。 
puts message.slice(1, 3)   

最後に発展編です。「"Why do birds suddenly appear...」というカンペーターの歌詞の中で、「you」が含まれる行、行末に「,」が含まれる行を検出しましょう!一つ一つ処理を追えば簡単です。

str = "Why do birds suddenly appear
Every time you are near?
Just like me,
They long to be
Close to you."

# 上の歌詞を行ごとに分割します。
lines = str.split(/\n/)

# "you"にマッチする行を表示します。
puts "youが含まれていた行"
lines.each{ |line|
    if line =~ /[Yy]ou/ then
        puts line
    end
}

puts ""

# 行末に","がある行を表示する
puts "行末にカンマが含まれていた行"
lines.each { |line|
    if line =~ /,$/ then
        puts line
    end
}

# =>実行結果
# youが含まれていた行
Every time you are near?
Close to you.

# 行末にカンマが含まれていた行
Just like me,

以上です。正規表現は、なんとなく使えている方が多い(自分も...)と思いますので、 自責の念も込めて、まとめました。これからも、過去の自分が助けてくれるように理解した内容を留めていきたいと思います。