はじめに
あなたは正規表現は得意ですか??
「はい、得意です!」と答える人は、そんなに多くないのかな?と思います。
自分は特に苦手で嫌いでした。。
このままではダメだと思い、正規表現の壁に立ち向かいました。
自分がもがいて手に入れた情報で、皆さんに少しでもいい情報を届けたれたら幸いです!!
※一番下に、正規表現に用いる記号一覧を載せてあるので参考にしてください!
正規表現とは
文字列とのパターンマッチを行うための小さな言語のことです。
正規表現を用いることで、ある文字列があるパターンに該当する文字列を含んでいるかを確認したり、パターンに該当する部分を検索できます。
パターンマッチ(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にマッチ |