woshidan's blog

あいとゆうきとITと、とっておきの話。

今日からはじめよう、正規表現 in Ruby(アンカー編)

アンカーで文字列の先頭や末尾を指定する

初心者:アンカーってそんなのありましたっけ? いま、はじめて聞いたんですけど。

 

君、いつも使ってますよ。

ほら、ここの「^」とか。これは行頭って意味のアンカーです。

/^\d+/.match("12: 1234") => #<MatchData "12">

初心者:ああ、なるほど。これだけですか?

 

いえ、他にもありますよ。

http://docs.ruby-lang.org/ja/1.9.3/doc/spec=2fregexp.htmlからそのまま引用すると、

  • ^ 行頭にマッチします。行頭とは、文字列の先頭もしくは改行の次を意味します。
  • $ 行末にマッチします。 行末とは文字列の末尾もしくは改行の手前を意味します。
  • \A 文字列の先頭にマッチします。
  • \Z 文字列の末尾にマッチします。 ただし文字列の最後の文字が改行ならばそれの手前にマッチします。
  • \z 文字列の末尾にマッチします。
  • \b 単語境界にマッチします。 単語を成す文字と単語を成さない文字の間にマッチします。 文字列の先頭の文字が単語成す文字であれば、文字列の先頭 の位置にマッチします。
  • \B 非単語境界にマッチします。 \bでマッチしない位置にマッチします。

の7つがあります。

 

初心者:……。

 

アンカーという言葉を認識したのもさっきですし、

抽象的なものが並んでいたらはじめは難しく感じるのは分かっていますから、

非単語境界の文字をそんな悲しそうな顔で見つめないでください。

 

一つずつ、確認していきましょう。

文字列のどこか一カ所を指定する記号なので、一つの正規表現中でそんなやたらめったら使うことはあなたがきちんと理解していけばないでしょうし、

irbに打ちこんで確認しながら進めていいですから。

 

^ 行頭にマッチ 「○○が最初に来ないんだったら見ない」

これは、正規表現にマッチする文字以外が、行のはじめに存在していたら、その後ろに正規表現にマッチする文字が存在していてもマッチさせない、という意味です。

/^\d+/.match("1234") #=> #<MatchData "1234">
/^\d+/.match(" 1234") #=> nil
/\d+/.match(" 1234") #=> #<MatchData "1234">

たとえば、上の例の正規表現は、「行の頭に数字が来ている場合だけ、数字だけで出来ている文字列を可能な限り長くマッチさせる」 という意味です。

だから、

一番上の行頭から数字が続いている例では1234がマッチしていて、

次の行頭に半角スペースが入った例では後ろに数字の列があってもマッチしていません。

比較用に、^を外した場合も並べていますが、これは先頭に半角スペースがあっても後ろの1234とマッチしていますね。

 

初心者:へえ……。要するに、「○○が最初に来ないんだったら見ない」って言ってるんですね。

 

そうですね。

初心者:ところで、えーと、たしかこれ、[]の中なら数字以外って意味ですよね。

 

これ(^)は実はキャレットって名前がありますから、よければ覚えてあげてください。

それで^は文字クラスの中ではたしかに後ろに列挙した文字以外って意味ですね。

()の中ではただの文字「^」として扱われるので、そこも注意しましょう。 

$ 行末にマッチ 「見たいのは○○が最後にいるかだけというのを背後から主張する」

$は正規表現にマッチする文字列があるかどうか、行末から探します。行末とは文字列の末尾もしくは改行の手前を意味します。

 

初心者:あれ、この記号動きませんよ?

 

えーと、人の話は最後まで聞いてくださいね。

この記号は適用させたいパターンの後ろにつけます。

/$dollars/.match("  dollars") #=> nil
/dollars$/.match("  dollars") #=> #<MatchData "dollars">
/dollars$/.match("dollars  ") #=> nil

一番目と二番目の例を見てください。

一番目はパターンの前に$をおいてしまっているので、意味のないパターンになっていて、行の末尾においたdollarsとマッチングさせることができていません。

二番目の例のように行末にマッチングさせたいパターンの後ろに$をおいてください。

また、三番目と二番目の例を確認して、たしかに行末にdollarsがある場合しかマッチしていないことを理解して下さい。

 

初心者:これももしかして、ビリオンとかかっこいい名前がありますか

 

いいえ、ふつうにドルマークか、ドルの複数形、ダラスですね。

なんとなく使える以上の興味を持つ事はいいことですが、かっこいいとかそういう問題ではないのですよ……。

あと、ストーカーみたいな見出しはやめてくださいね。

 

\A 文字列の先頭にマッチ

\Aは文字列の先頭にマッチします。これも、こんな感じで正規表現の先頭に置きます。

 

初心者:これ、^と何が違うんですか?

 

そうですね、一行しかない文字列を扱うとき、実は^と実質おなじ働きをします。

/^string/.match("string") #=> #<MatchData "string">
/\Astring/.match("string") #=> #<MatchData "string">
/^string/.match(" string") #=> nil
/\Astring/.match(" string") #=> nil

こんな感じです。

 

初心者:じゃあ、二行以上書いた場合は違うんですか。

 

その通り。こんな例をあげてみるとわかりやすいでしょうか。

/^abcd/.match("bcde\nabcde") #=> #<MatchData "abcd">
/\Aabcd/.match("bcde\nabcde") #=> nil

マッチングの対象の文字列は一行目はabcdにマッチしないのだけど、二行目の行の先頭はabcdにマッチします。

一つ目の正規表現は、行の先頭を見るから、一行目の先頭だけでなく、二行目の先頭も見るので、二行目のabcdがマッチしています。

二つ目の正規表現は、文字列の先頭しか見ないから、一行目の先頭があってなかったら、それ以降はもう見ない。だから二行目のabcdはマッチせず、返り値はnilになります。

 

初心者:なるほどー。使いどころがよく分かりませんけど。

 

そうですね、^と比べて他のアンカーはあまり使わないかもね。

でも、これは慣れるための練習だから一通りさらっておきましょう。

 

\Z 文字列の末尾にマッチ

\Z 文字列の末尾にマッチします。 ただし文字列の最後の文字が改行ならばそれの手前にマッチします。

\z 文字列の末尾にマッチします。

これらは、文字列の末尾にマッチします。文字列の末尾にマッチする記号なので、正規表現の最後におきます。

と言えば、もう分かりますか。

初心者:とりあえず、これも、末尾においてみたんだけど、なんだかすごい落ち着かないです。

/string\Z/.match(" string") #=> #<MatchData "string">

とりあえず、おいてみたって不安な事言わないでくれますか。

気持ちは分かりますけど。……次の例を見てください。

/string\Z/.match(" str\n string\n") #=> #<MatchData "string">
/string\z/.match(" str\n string\n") #=> nil
/string\n\z/.match(" str\n string\n") #=> #<MatchData "string\n">
/string$/.match(" str\n string\n") #=> #<MatchData "string">
/string$/.match(" str\n string") #=> #<MatchData "string">

一行目は\Zなので、文末に改行文字(\n)があっても、その\nを無視してマッチングを行っていますが、\zだと\nを無視しないので、マッチが出来ていません。

三行目のように\zの前のリテラルに\nを加えるとマッチするようになります。

四行目、五行目では先ほどあつかった$が\nに関係なく作用している事を確認してください。

 

二つまとめて紹介した方がいいかと思って個別に見出しをつけていないのだけど、\Aが文字列の先頭,\Zが文字列の末尾なのは、 A,Zがアルファベットの最初と最後の文字だからですね、たぶん。

 

\b 単語境界にマッチ  端っこのどっちを見るかは自分で立って決める

\b 単語境界にマッチします。 

この正規表現は以下のように書きます。

/\bword/.match("beautiful-word") # => #<MatchData "word">

先頭に置いた場合bと単語が連続するので少し妙に感じるかもしれませんが、マッチングする単語(word)をまず検索した後、マッチした部分がマッチング対象の単語(beautiful-word)の境界(スペースや.,-で区切られた文字列の最初か最後)にあったら、マッチした文字列を返します。

 

また、\bさえ書いておけば単語の先頭でも末尾でもマッチする、というわけでなく、

先頭にマッチするか、末尾にマッチするかは\bを単語の前に置くか、後ろにおくかで決めます。

こんな感じですね。

/\babc/.match("is abcd?") #=> #<MatchData "abc">
/\babc/.match("isabcd?") #=> nil
/abc\b/.match("dabc is") #=> #<MatchData "abc">
/abc\b/.match("dabcis") #=> nil

 

注意してほしいことは、\Bは\bの否定で、マッチングさせたい単語が単語境界でなかった場合にマッチングが成功する事です。正規表現の略記法と同じだと思えば覚えやすいですが、\Zと\zとは意味が違うので、注意しましょう。

 

初心者:はーい。