はじめに
あなたは正規表現は得意ですか??
「はい、得意です!」と答える人は、そんなに多くないのかな?と思います。
自分は特に苦手で嫌いでした。。
このままではダメだと思い、正規表現の壁に立ち向かいました。
自分がもがいて手に入れた情報で、皆さんに少しでもいい情報を届けたれたら幸いです!!
※一番下に、正規表現に用いる記号一覧を載せてあるので参考にしてください!
正規表現とは
文字列とのパターンマッチを行うための小さな言語のことです。
正規表現を用いることで、ある文字列があるパターンに該当する文字列を含んでいるかを確認したり、パターンに該当する部分を検索できます。
パターンマッチ(Ruby)
Regexp#=~
正規表現にマッチした位置を返すときは、=~を使用する
# マッチしたら、その位置を返す
/[0-9]/ =~ 'ruby6' #=> 4
# マッチしない場合はnilを返す
/[0-9]/ =~ 'ruby' #=> nil
Regexp#===
あるパターンにマッチするかを調べるには、===を使用する
# 0〜9の数字を含んでいるか
/[0-9]/ === 'ruby' #=> false
/[0-9]/ === 'ruby6' #=> true
Regexp#match
マッチすればMatchDataオブジェクトを返し、しなければ「nilを返す
str = 'ruby6'
if matched = /[0-9]/.match(str)
p matched #=> "#<MatchData \"6"\>"
end
MatchDataから得られる情報は以下の通りです。
| MatchData | 変数 | 得られる値 |
|---|---|---|
| MatchData#[0] | $& | 直前にマッチした文字列 |
| MatchData#[1] | $1 | 直前にマッチした正規表現の中の、最初の括弧にマッチした文字列 |
| MatchData#pre_match | $` | 直前にマッチした文字列より前の文字列 |
| MatchData#post_match | $' | 直前にマッチした文字列より後の文字列 |
式展開時の注意点
正規表現で、式展開を用いる際は必要に応じてエスケープしましょう!
# エスケープする必要のある文字列
part = Regexp.escape('(incomplete)')
/[^.]+#{part}\.txt/ #=> /[^.]+\(incomplete\)\.txt/ # 括弧がエスケープされる
文字クラス
[]で囲むことで文字クラスの指定が可能です。
[a-z] #=> aからzまでの文字
[\w] #=> 英数字a(A)~z(Z)、0~9
量指定子
直前パターンの繰り返しをします。
# 郵便番号の正規表現の場合
post_num = /\d{3}-\d{4}/
post_num === '123-4567' #=> true
先頭と末尾
先頭には\A、末尾には\zとして表現する。
# 携帯電話番号の正規表現の場合
phone_num = /\A\d{3}-\d{4}-\d{4}\z/
phone_num === '012-3456-7890' #=> true
指定位置からの正規表現
指定された文字列 str に対して 位置 pos から自身が表す正規表現によるマッチ
/R.../.match?("Ruby") #=> true
/R.../.match?("Ruby", 1) #=> false
$& #=> nil
グルーピングと後方参照
()によりグルーピングをすることが可能。
後で、\1や\2のように参照することを後方参照と言います。
# 基本的な後方参照
/(text)\ to\ \1/ === 'text to text' #=> true
# ラベル付きグルーピングでの後方参照
/(?<num>[0-9+])[a-c\-]+\k<num>/ === '123-abc-123' #=> true
部分式呼び出し
後方参照と似ているが、グルーピングを再利用できる部分式呼び出しという参照方法がある。
# 部分式呼び出しの「\g<1>」は最初のグルーピングの式「[0-9+]」で置換される
/([0-9+])-\g<1>-\g<1>/ === '012-3456-7890' #=> true
先読みと後読み
先読みとは、(?<=sample)は「"sample"という文字列と一致する箇所の直後」を表します。
また、後読みはその逆で直前になります。
また、否定文に関しては、文字通り肯定先(後)読みと逆の動作をします。
# 肯定先読み
'sampleApp'.scan(/sample(?=App)/) #=> ["sample"]
# 肯定後読み
'sampleApp'.scan(/(?<=sample)App/) #=> ["App"]
バックトラックの抑止
(?>)で正規表現を囲むことで、そこにマッチした部分でバックトラックを抑止できる。
/(?>\w+)[0-9]/ === 'ruby6' #=> false
オプションの指定
以下の表を用いて、オプションを指定することでパターンマッチの挙動を変化させることが出来ます。
| オプション | 意味 |
|---|---|
| i | 大文字、小文字の区別をしない |
| o | 正規表現リテラルが最初に評価された際に、一度だけ式展開を行う |
| x | 正規表現中の「#」を無視して、コメントとみなす |
| m | 「.」に改行もマッチさせる、複数行モード |
| u | 正規表現をUTF-8として解釈 |
| s | 正規表現をShift-JISとして解釈 |
| e | 正規表現をEUC-JPとして解釈 |
| n | 正規表現をASXIIとして解釈 |
# オプション無し
%w(foo bar).map { |str| /#{str}/ } #=> [/foo/, /bar/]
# オプション有り
%w(foo bar).map { |str| /#{str}/o } #=> [/foo/, /foo/]
終わりに
正規表現は必ずと言っていいほど、最初はハマります。。
暗号のように見えてきて気が狂いそうですが、この記事を機に慣れていってくれることを願っています!
宣伝
想像以上の反響をいただき、ありがとうございます!!
よかったら、自分のTwitterでも、有益な情報を発信していますので、興味があればフォローしていただけると嬉しいです!
参考(正規表現に用いる記号一覧)
| 1 | 2 |
|---|---|
| ^ | 行頭、改行文字の直後にマッチ |
| $ | 行末、改行文字の直前にマッチ |
| \A | 文字列の先頭にマッチ |
| \Z | 文字列の末尾にマッチ、改行文字なら改行の直前 |
| \z | 文字列の末尾にマッチ、改行でも常に末尾にマッチ |
| \w | 英数字[0-9A-Za-z_]にマッチ |
| \W | 英数字[0-9A-Za-z_]以外にマッチ |
| \s | 空白文字[\t\r\n\f]にマッチ |
| \S | 空白文字[\t\r\n\f]以外にマッチ |
| \d | 数字[0-9]にマッチ |
| \D | 数字[0-9]以外にマッチ |
| \h | 16進数に使われる文字[0-9A-Fa-f]にマッチ |
| \b | []の外では単語の境界、中ではバックスペースにマッチ |
| \B | 単語の境界以外にマッチ |
| \G | 直前にマッチした箇所の直後にマッチ |
| . | 改行を除く任意の1文字にマッチ |
| [] | 文字クラスの指定 |
| [0-9] | すべての数字にマッチ |
| [^0-9] | すべての数字以外にマッチ |
| * | 直前の正規表現の0回以上の反復、できるだけ長くマッチ |
| + | 直前の正規表現の1回以上の反復、できるだけ長くマッチ |
| *? | 直前の正規表現の0回以上の反復、より短くマッチ |
| +? | 直前の正規表現の1回以上の反復、より短くマッチ |
| ? | 直前の正規表現の0〜1回の反復 |
| ?? | 直前の正規表現の0〜1回の反復、より短くマッチ |
| {m} | 直前の正規表現のm回の反復 |
| {m}? | 直前の正規表現のm回の反復、より短くマッチ |
| {m,} | 直前の正規表現のm回以上の反復 |
| {m,}? | 直前の正規表現のm回以上の反復、より短くマッチ |
| {m,n} | 直前の正規表現のm〜n回の反復 |
| {m,n}? | 直前の正規表現のm〜n回の反復、より短くマッチ |
| () | グループ化 |
| \n | 後方参照、正規表現のn番目のグループにマッチした文字列にマッチ |
| \k< label name > | 後方参照、ラベルでマッチ |
| (?:) | グループ化、後から参照出来ない |
| (?< label name >) | ラベル付きグループ化 |
| (?#) | コメント |
| (?=) | 先読み |
| (?!) | 否定先読み |
| (?<=) | 後読み |
| (?<!) | 否定後読み |
| (?>) | バックトラック抑止 |
| (?ixm-ixm) | オプションのon/off |
| (?ixm-ixm:) | 括弧内のオプションのon/off |
| a | b | aもしくはbにマッチ |