🦓

[Ruby]正規表現

2023/09/17に公開

はじめに

正規表現(Regular ExpressionsまたはRegEx)は、文字列のパターンを記述し、特定のテキストパターンを検索、一致、置換、抽出するためのなツールです。

https://docs.ruby-lang.org/ja/latest/doc/spec=2fregexp.html

こちらのサイトでRubyの正規表現の検証を行うことができます。
https://rubular.com/

正規表現の作成

  1. 正規表現はスラッシュ(/)で囲むか、Regexp.newを使用して作成します。

パーセント記法で生成することも可能であり、この場合は「%r」を指定します。これらの値はRegexpクラスのインスタンスになります。

/Ruby/#=>/Ruby/
%r(Ruby)#=>/Ruby/
Regexp.new"Ruby"#=>/Ruby/

正規表現記号

正規表現記号を使用することで、テキスト内で特定のパターンを検索、抽出、置換するなどの操作が可能です。文字列処理やパターンマッチングに使用されます。

正規表現特殊文字 説明
. 任意の1文字に一致 a.b"axb" に一致します。
* 直前の文字またはグループが0回以上繰り返す ab*c"ac""abbc" に一致します。
+ 直前の文字またはグループが1回以上繰り返す ab+c"abc""abbbc" に一致します。
? 直前の文字またはグループが0回または1回繰り返す ab?c"ac""abc" に一致します。
` ` OR条件で一致
[] 文字の集合で一致 [aeiou]"a""e" に一致します。
() グループを作成し、キャプチャ対象を指定する (abc)+"abcabc" に一致します。
^ 文字列の先頭に一致 ^start"start of string" に一致します。
$ 文字列の末尾に一致 end$"the end" に一致します。
\ 特殊文字のエスケープ \\\ に一致します。

マッチした文字列を取り出すには「$&」を参照します:

&$

マッチした文字列より前の文字列を参照するには($+バッククォート):

$`

マッチした文字列より後の文字列を参照するには「$'」($+シングルクォート)を使用します:

$'

この三つをセットで覚えましょう。

/bb/=~"aabbcc"
p $` #=>"aa"
p $& #=>"bb"
p $' #=>"cc"

記号だけだと分かりにくいので例を通して理解することをおすすめします。

正規表現特殊文字

特殊文字を使用することで、正規表現パターンをより具体的に指定し、文字列内の特定のパターンを検索、抽出、または置換することができます。例えば、\d+は1つ以上の数字に一致し、\Aは文字列の先頭に一致します。

正規表現特殊文字とその説明を表にまとめました。

特殊文字 説明 正規表現パターン例
\d 数字に一致 \d+ /\d/ === "123"
\D 数字以外の文字に一致 \D+ /\D/ === "Hello, World!"
\w 単語文字(英字、数字、アンダースコア)に一致 \w+ /\w+/ === "user123" # => true
\W 単語文字以外の文字に一致 \W+ /\W+/ === "!@#$%"
\s 空白文字(スペース、タブ、改行など)に一致 \s+ /\s+/ === " " # => true
\S 空白文字以外の文字に一致 \S+ /\S+/ === "Hello,World!"
\A 文字列の先頭に一致 \AHello /\AHello/ === "Hello, World!"
\z 文字列の末尾に一致 World!\z /World!\z/ === "Hello, World!"
\Z 文字列の末尾または最後の改行の前に一致 World!\Z /World!\Z/ === "Hello, World!\n" # => true

繰り返し

正規表現記号 意味 一致する文字列の例
* 直前の要素が0回以上繰り返す ab*c "ac", "abc", "abbc"
+ 直前の要素が1回以上繰り返す ab+c "abc", "abbc"
? 直前の要素が0回または1回繰り返す colou?r "color", "colour"
{n} 直前の要素がちょうどn回繰り返す a{3} "aaa"
{n,} 直前の要素が少なくともn回以上繰り返す a{2,} "aa", "aaa", "aaaa"
{n,m} 直前の要素がn回以上、最大でm回繰り返す a{2,4} "aa", "aaa", "aaaa"

一致の確認

正規表現を使用して文字列がパターンに一致するかどうかを確認できます。
=~演算子を使用します。

text = "The quick brown fox"

# 正規表現と一致する部分文字列の開始インデックスを取得
if /quick/ =~ text
  index = Regexp.last_match.begin(0)
  puts "一致しました。一致した部分の開始インデックス: #{index}"
else
  puts "一致しませんでした"
end
# 一致しました。一致した部分の開始インデックス: 4

正規表現/quick/と文字列textが一致する部分があるため、=~演算子がtrueを返します。そして、Regexp.last_match.begin(0)を使用して、一致した部分文字列の開始インデックスを取得し、それを表示しています。

Regexp.last_match.begin(0)は、一致した最初のキャプチャグループ(インデックス0)の開始インデックスを返します。
一致しなかった場合、=~演算子はnilを返すため、条件分岐によって"一致しませんでした"が表示されます。

もし複数のキャプチャグループがある場合、それぞれのキャプチャグループの開始インデックスを取得したい場合は、対応するインデックスを指定します。

text = "Name: John, Age: 30"

# 正規表現で名前と年齢をキャプチャ
match_data = /Name: (\w+), Age: (\d+)/.match(text)

if match_data
  # キャプチャグループ1(名前)の開始インデックスを取得
  name_start_index = match_data.begin(1)
  # キャプチャグループ2(年齢)の開始インデックスを取得
  age_start_index = match_data.begin(2)
  
  puts "名前の開始インデックス: #{name_start_index}"
  puts "年齢の開始インデックス: #{age_start_index}"
else
  puts "一致しませんでした"
end

# 名前の開始インデックス: 6
# 年齢の開始インデックス: 17

正規表現/Name: (\w+), Age: (\d+)/を使用して、名前と年齢をキャプチャしています。match_dataには一致した情報が含まれており、beginメソッドを使用してキャプチャグループ1(名前)とキャプチャグループ2(年齢)のそれぞれの開始インデックスを取得しています。

\wはアルファベット(大文字と小文字)、数字、アンダースコア(_)など、ワード文字を表します。一般的に英数字やアンダースコアを指します。
+は直前の文字(\w)が1回以上繰り返されることを示します。つまり、1回以上のワード文字が連続していることを表します。"John123""user_name"などの文字列に一致します。

\dは数字を表します。0から9までの任意の数字に一致します。
+は直前の文字(\d)が1回以上繰り返されることを示します。つまり、1回以上の数字が連続していることを表します。"12345""2023"などの文字列に一致します。

このように複数のキャプチャグループの開始インデックスを取得できます。
キャプチャグループが増えるにつれて、対応するインデックスを指定すれば取得できます。

https://docs.ruby-lang.org/ja/latest/method/Regexp/s/last_match.html

一致した部分の抽出

一致した部分文字列を抽出したい場合、matchメソッドやscanメソッドを使用します。

text = "Hello, my email is example@email.com"
match = /(\w+@\w+\.\w+)/.match(text)
puts match[0] # "example@email.com"

https://docs.ruby-lang.org/ja/latest/method/String/i/match.html

一致した部分を配列で返す

文字列に対して正規表現を用いてマッチする部分を検索し、それらを配列として返すメソッドです。

string = "Hello, World! This is a sample text."

# 正規表現パターンに一致する文字列を検索し、配列として返す
matches = string.scan(/\w+/) # /\w+/ は単語に一致する正規表現パターン

puts matches
["Hello", "World", "This", "is", "a", "sample", "text"]

scanメソッドは以下の特徴を持っています:

  1. マッチした部分文字列を配列に格納して返します。
  2. マッチしない場合、空の配列を返します。
  3. マッチした部分文字列が複数ある場合、すべてのマッチを取得します。

https://docs.ruby-lang.org/ja/latest/method/String/i/scan.html

文字列の置換

gsubメソッドを使用して、正規表現に一致する部分文字列を置換できます。

text = "apple banana apple"
new_text = text.gsub(/apple/, "orange")
puts new_text # "orange banana orange"

https://docs.ruby-lang.org/ja/latest/method/String/i/gsub.html

選択

複数のパターンの中から一つを選択することになります。選択は、正規表現内でパイプ記号 | を使用して定義されます。パイプ記号を使用することで、複数の異なるパターンを指定し、文字列がいずれかのパターンに一致するかどうかをチェックできます。

いずれか満たす場合:

/^(aa|bb)c$/
"aac" #=>true
"bbc" #=>true

文字集合

文字集合は特定の文字のグループを表すために使用されます。文字集合は [ ] 内に文字を列挙することで定義され、その中のいずれかの文字と一致することになります。

  1. [0-9]: 0から9までの数字に一致します。任意の数字に一致する場合に使用されます。
  2. [A-Za-z]: 英大文字と英小文字のすべてのアルファベット文字に一致します。
  3. [a-zA-Z0-9]: 英数字(英大文字、英小文字、数字)に一致します。
  4. [aeiouAEIOU]: 英語のすべての母音文字に一致します。
  5. [^0-9]: 数字以外のすべての文字に一致します。キャレット (^) を使用して否定的な文字集合を表現します。

文字の集合に対象となる文字:

/a[bcd]e[fg]h/
"abegh"#=>true
/a[1-5]z/==="a2z" #=>true
/a[b-d]z/==="abz" #=>true

マッチ結果を返す

前方一致

前方一致しているかどうかを判定し、trueまたはfalseを返す場合、Stringクラスのstart_with?メソッドを使うことができます。
一致する場合はtrueを、一致しない場合はfalseを返します。

string = "Hello, World!"

# 前方一致を判定
result = string.start_with?("Hello") # true
result2 = string.start_with?("World") # false

puts result
puts result2

https://docs.ruby-lang.org/ja/latest/method/String/i/start_with=3f.html

後方一致

文字列の最後が指定した文字列で終わるかを判定するために、end_with?メソッドを使うことができます。
指定された文字列が対象の文字列の末尾に一致する場合に true を返し、一致しない場合に false を返します。

string = "Hello, World!"

# 最後が後方一致するか判定
end_result = string.end_with?("World!") # true

puts end_result

https://docs.ruby-lang.org/ja/latest/method/String/i/end_with=3f.html

正規表現オプション

オプションは正規表現を柔軟に扱うために使用されます。
例えば、大文字小文字を区別しないマッチング (i) や複数行モード (m) は、テキスト処理でよく使用されます。

オプション 説明
i 大文字と小文字を区別しないマッチング /abc/i"ABC" にも一致
m 複数行モード。^$ が各行の先頭と末尾にマッチ /^abc/m"abc\nABC" の各行に一致
x 正規表現内の空白とコメントを無視 / a b c /x"abc" に一致
n カスタム名前空間が設定された正規表現を有効化 /abc/n はカスタム名前空間内の正規表現にマッチ
u ユニコードサポートを有効化 /ü/u"ü" に一致
s ドット . が改行文字にも一致 /a.b/s"a\nb" に一致
/ruby/==="RUBY"#=>false
/ruby/i==="RUBY"#=>true
/a.*b/==="a\nb"#=>false
/a.*b/m==="a\nb"#=>true

終わりに

ただただ正規表現の勉強メモです。

Discussion