Ruby 初心者が正規表現を使う
ファイルの文字列を整形したい時などにRubyで正規表現を使って、文字列整形することがあったので、忘れないように学習した点をまとめました。
Rubyは初心者レベルで、正規表現もあまり詳しい方ではないので、同じ境遇の方向けの記事となります。
スポンサーリンク
目次
正規表現とは?
簡単に言ってしまうと「いくつかの文字列を一つの形式で表現するための記法」あやふやな文字についても、扱えるように促す表現方法。
例えば、abcか判定してみましょう。
# abcを表現 pattern = /abc/ puts pattern === "abc" # true puts pattern === "abcd" # true puts pattern === "abd" # false
文字列の部分一致の場合もtrueとなります。
正規表現扱い方
自分は初心者のため、そもそもここからスタートしました。
正規表現難しそう?って思う方も多いかもしれませんが、お作法を把握すればさほど怖くなくなると思います。
まず、正規表現は「/」(スラッシュ)でくくるだけです。
簡単簡単!!今のところは、、、
/パターン/
Regexpクラス
正規表現のオブジェクトを生成するのにRegexpクラスのメソッドを使うこともできます。
Regexpクラスのクラスメソッドには「new」や「compile」メソッドがあります。
例)newを使って正規表現のオブジェクトを生成する。
Regexp.new("Hellow Ruby")
上記のオブジェクトは、以下のオブジェクトと同様となります。
/Hellow Ruby/
new / compileメソッドの引数は、以下となります。
new(string, option = nil, code = nil) -> Regexp compile(string, option = nil, code = nil) -> Regexp
(必須)string :正規表現を文字列として指定する。
(任意)option :Regexp::IGNORECASE、Regexp::MULTILINE, Regexp::EXTENDED 修飾子を指定する。
(任意)code :マッチするかどうかを判定する時に使用される文字コードを指定する。
compileメソッドについても、newと同じ使い方になります。
メタ文字
正規表現内部で特殊な働きをする文字で、その文字としてマッチさせるためには、 バックスラッシュ「\」を前に付与する。
「\\」のようにダブルでバックスラッシュを記述するとバックスラッシュ1文字に マッチします。
( ) [ ] { } . ? + * | \
例)「.」は改行を除いた任意の1文字を意味します。
正規表現の記法
正規表現を学ぶにあたって、よく使いそうな特殊な記法についてある程度把握しておいたほうがいいなと思いまとめました。
省略記法について
ソースの可読性を上げるため、省略記法が使われるようです。
レビューする際に他の人が使っていることもあるので、覚えておく必要はあります。
また、自分でも積極的に使用できたらと思います。
メタ文字列 | 説明 |
---|---|
\w | 英数字 [a-zA-Z0-9_] |
\W | 非英数字 [^a-zA-Z0-9_] |
\s | 空白文字 [ \t\r\n\f\v] |
\S | 非空白文字 [^ \t\r\n\f\v] |
\d | 10進数字 [0-9] |
\D | 非10進数字 [^0-9] |
\h | 16進数字 [0-9a-fA-F] |
\H | 非16進数字 [^0-9a-fA-F] |
繰り返し文字について知ろう
正規表現で繰り返しを表す「量指定子」という表現方法があります。
「量指定子」を利用することで、直前のパターンの繰り返しを表現することができます。
以下のような量指定子が存在する。
量指定子 | 説明 |
---|---|
* | 直前の正規表現の0回以上の反復 |
+ | 直前の正規表現の1回以上の反復 |
? | 直前の正規表現の0回または1回の反復 |
{n} | 直前の正規表現のn回までの反復 |
{m, n} | 直前の正規表現のm回~n回までの反復 |
例えば、電話番号などを量指定子で表現すると以下のようになる
例)070-1234-1234
# 数字が3回反復、数字が4回反復、数字が4回反復 pattern = /\d{3}-\d{4}-\d{4}/ puts pattern === "070-1234-1234" # true puts pattern === "x7x-1234-1234" # false
先頭と末尾の表現(アンカー)
行や文字列で特定の位置を対象としたい場合、アンカーでマッチングさせることができます。
よく使うアンカーをまとめてみました。
表記 | 説明 |
---|---|
^ | 行頭にマッチします。 |
$ | 行末にマッチします。 |
\A | 文字列の先頭にマッチします。 |
\Z | 文字列の末尾にマッチします。ただし文字列の最後の文字が改行ならば 手前にマッチ |
\z | 文字列の末尾にマッチします。 |
\b | 単語の区切りにマッチします。 |
\B | 単語の区切り以外の区切り(単語内の文字区切り)にマッチします。 |
個人的に「\b」があまり理解できなかったため、実際にサンプルを書いて結果を出力してみました。
# 文字列中の real にマッチする puts /hot/.match("hotdog") # => #<MatchData "hot"> # 先頭に hot マッチする puts /\Ahot/.match("hotdog") # => #<MatchData "hot"> # 先頭に hot ではないのでマッチしない puts /\Ahot/.match("testhotdog") # => nil # 単語境界がhotの前にないのでマッチ puts /\bhot/.match("hotdog") # => #<MatchData "hot"> # 単語境界がhotの前にないのでマッチしない puts /\bhot/.match("testhotdog") # => nil
先読み、後読み(lookahead, lookbehind)
先読みなどの記法では、(?=からパターンを記述)して利用する。
# 以下のように(?=から)の間にパターンを設定する /パターン(?=先読みパターン)パターン/
例)
str = "アイウエオカキクケコ" puts str.scan(/アイウエオ(?=カキクケコ)/) # => アイウエオ ## scanは、正規表現のパターンとマッチする部分を文字列からすべて取り出し、配列にして返します。
表記 | 説明 |
---|---|
(?=) | 先読み |
(?< =) | 後読み |
(?!) | 否定の先読み |
(?< !) | 否定の後読み |
メタ文字列のエスケープ
正規表現パターンではバックスラッシュを使ってエスケープシーケンスも記述することができます。
表記 | 説明 |
---|---|
\t | タブ |
\n | 改行 |
\r | キャリッジリターン |
\f | 改ページ |
\b | バックスペース |
\e | エスケープ |
\s | 空白 |
\nnn | 8 進数表記 |
\xnn | 16 進数表記 |
\cx | コントロール文字 (x は ASCII 文字) |
\C-x | コントロール文字 (x は ASCII 文字) |
例)途中改行を含めてマッチするか
puts str = "aa\nbb" # => aa # => bb puts str.match(/aa\nbb/) # => aa # => bb
puts str = "aa bb" # => aa # => bb puts str.match(/aa\nbb/) # => aa # => bb
2パターンとも同じ結果になることを確認できた。
キャプチャについて
丸括弧 ( ) によってキャプチャをすることができます。
括弧に囲まれた正規表現でマッチした前からn番目の開き括弧によって囲まれた部分式にマッチした 文字列を後で参照することができます。
/(取得したい正規表現)/
例)日付が正規表現でマッチするか
today = '2017-10-01' # パターン、ハイフン区切りで、数字4回反復、数字2回反復、数字2回反復 testRegexp = /(\d{4})-(\d{2})-(\d{2})/ # 正規表現オブジェクト =~ string形式 # 正規表現オブジェクトが対象の文字列にマッチしているかどうか(位置が返る) puts testRegexp =~ today # => 0 # $1から連番で、丸括弧で指定した部分が参照できる puts $1 # => 2017 puts $2 # => 10 puts $3 # => 01
名前付きキャプチャについて
キャプチャでn番目を取得だと数が多くなったりしてくると結構辛かったり、何を意味しているものだったけと後でメンテする際に厳しい面がある。
そんな時にはキャプチャに名前を付けることができます。
丸括弧内で、以下のように名前をつけることで、後で参照できる
?<name>
例)日付が正規表現でマッチするか
today = '2017-10-01' # year(年)、month(月),date(日)で名前をつける testRegexp = /(?<year>\d{4})-(?<month>(\d{2}))-(?<date>(\d{2}))/ testRegexp =~ today # マッチした文字列を表示 puts Regexp.last_match[:year] # => 2017 puts Regexp.last_match[:month] # => 10 puts Regexp.last_match[:date] # => 01 # (余談)名前なしでも連番でも取得可能 puts Regexp.last_match[0] # => 2017-10-01 puts Regexp.last_match[1] # => 2017 puts Regexp.last_match[2] # => 10 puts Regexp.last_match[3] # => 01
個人的には名前が付いていたほうが、何を意味しているのかわかりやすいので、ソースの可読性は上がると思いました。
正規表現よく使うメソッド
正規表現関係でよく使うメソッドを一部まとめてみました。
・match
(format)str.match(pattern)
マッチしたときはMatchDataオブジェクトを返し、マッチしなかったときはnilを返します。
regStr = Regexp.new("昨日、今日、明日") puts regStr.match("昨日、今日、明日") # => 昨日、今日、明日 puts regStr.match("昨日") # => nil
・sub
(format)str.sub(pattern, replacement)
マッチした最初の部分を文字列に置換します。
str = "今日も一日ご苦労様" puts str.sub("今日", "明日") # => 明日も一日ご苦労様
・gsub
(format)str.gsub(pattern, replacement)
正規表現のパターン(pattern)にマッチした全ての部分を文字列(replacement)に置換します。
str = "今日も一日ご苦労様" puts str.gsub("日", "全部変更") # => 今全部変更も一全部変更ご苦労様
まとめ
Rubyでの正規表現は、使い方もシンプルで、便利なメソッドも揃っているため、そもそも正規表現自体をあまり把握していなくて、扱いやすいと思いました。
今まで必要な時だけ調べては、正規表現を利用していましたが、今後は積極的にマスターしていければ、いろんな文字列処理を素早く組めるような気がしました。
みなさんも是非、一度お試しください。