- ベストアンサー
置換演算子についての疑問
お世話になっております。horagaiです。質問160286 http://oshiete1.goo.ne.jp/kotaeru.php3?q=160286 と同じようなことをやりたかったので回答#2のやり方を 試してみました。そこでいろいろ疑問が出てきたので教えていただきたいと思います。 (1) $num=<> ; while($num =~ s/(.*\d)(\d\d\d)/$1,$2/g){;} print "num=$num\n"; とするとたしかにうまくいくのですが、これでうまくいく理由がわかりません。 置換演算子が後ろからパターンマッチをしていくのだとすればわかるのですが。 前からだとするとたとえば123456は最初に(1)(234)56で引っかかって1,23456 。 次に (1,2)(345)6 で引っかかって1,2,3456 ・・・。 などとなりそうな気がします。 (2) またwhileを使わずに $num =~ s/(.*\d)(\d\d\d)/$1,$2/g; としてみると 例えば入力が 12345678 とすると num=12345,678 と最初の3桁しか区切ってくれません。マニュアルを見ると 「gオプションは出現したパターンをすべて置換する。」 と書いてあるのにどうしてでしょう。 前からマッチするせよ後ろからマッチするにせよカンマが1つしか 入らないということはないと思うのですが。 以上、私が根本的な勘違いをしているかも知れませんので その辺のところもご指摘いただければ幸いです。 ちなみにOSはLinux.Perlのバージョンは5.004です。
- みんなの回答 (3)
- 専門家の回答
質問者が選んだベストアンサー
- ベストアンサー
えとですね。 正規表現には1つの原則がありまして。 たとえば、123456789だと、可能性として、 1,23456789 12,3456789 123,456789 1234,56789 12345,6789 123456,789 これらすべて、マッチする可能性がありますよね。 そういうパターンであることはわかりますか? で、正規表現は、「マッチする可能性の中から、もっとも文字列の長いものを採用する」という原則があります。 ですので、.* にひっかかる可能性のあるうちで、もっとも長い文字列である 12345 がひっかかってるわけです。 これは正規表現すべてで統一されていて、最長マッチといいます。 逆に、もっとも短い可能性を採用させるには、 (.*?\d)(\d\d\d) と、* のあとに ? を記述します。 もっとも、これをやってもおっしゃったような状況にはならず、 1,2345,6789 になりますが。 (これは、一度置き換えの対象になった部分は二度と検索しないという規則があるからです)
その他の回答 (2)
leaz024さんに補足していただいているようですが念のため。 >長さが一定になるようなパターンなら はい。 実際に試してみました。必ず先頭からです。 最後に $ を付けたら最後からになるかと思ったんですが、やってみたらやっぱり先頭からでした。 ですので、正規表現の検索は必ず先頭からってことになるようです。 >そういう解釈であっていますか? これも合ってます。はい。
お礼
いろいろとありがとうございました。 いままで掲示板ソフトなどで置換演算子を使ってみてもなかなか思い通りの 置換をしてくれず、その理由がわからなくて悩んでいたのですが 「マッチする可能性の中から、もっとも文字列の長いものを採用する」 「一度置き換えの対象になった部分は二度と検索しない」 の2つの原則を知っただけで置換演算子の振舞の謎はほとんど解けました。 おかげさまでこれからは悩むことも減りそうです。 また何かありましたら宜しくお願いします。
- leaz024
- ベストアンサー率75% (398/526)
最長マッチについては、deagleさんのアドバイスでご理解頂けていると思います。 $num="1234567890"とすると、1回目のマッチングでは「1234567,890」となりますね。 s///は「置換した回数」(この場合1)を返すので、置換が成功するとwhileの条件が真となり、また置換を行おうとします。 この繰り返しで、2回目で「1234,567,890」、3回目で「1,234,567,890」となり、ここでマッチしなくなるためs///が0を返し、whileが終了します。 下記HPが大変参考になりますので、ご一読ください。
お礼
ご回答ありがとうございます。 御紹介いただいたURLは知っていたのですが、「数字をコンマで区切る」 の項目には気がつきませんでした。たしかに参考になりました。 正規表現は奥が深いですね。
補足
な~るほど。質問の件についてはよーくわかりました。 >これらすべて、マッチする可能性がありますよね。 >そういうパターンであることはわかりますか? もちろんわかります。だから置換演算子が前からマッチさせていくとすると うまく行く理由がわからなかったのです。 >「マッチする可能性の中から、もっとも文字列の長いものを採用する」 >という原則があります。 この原則は知りませんでした。とにかく前から1文字ずつ読んでいって、マッチする パターンが最初に出現した時点で即置換するものだとばかり思っていましたが、 一度最後まで読んでからパターン検出をするのですね。 ですがこの原則はあくまで長さが不定のパターンに対する原則ですよね。 長さが一定になるようなパターンなら、例えば(\d\d)(\d\d\d)などであれば やはりパターンが最初に出現した部分にマッチすると考えてよいのでしょうか? また正規表現の上では長さ不定でも、たまたま与えられた文字列ではそのパターンに マッチするすべての場所が同じ長さになってしまった場合はどうなるのでしょう。 その場合も最初に出現した部分にマッチするのでしょうか? >一度置き換えの対象になった部分は二度と検索しないという規則があるからです なるほど。それで(2)の疑問、すなわちgオプションを付けても一度しか置換して くれない理由もわかりました。 最初に 「マッチする可能性の中から、もっとも文字列の長いものを採用する」 という原則にしたがって (12345)(678) でマッチさせ (12345),(678)とする。 すると (12345,678)はすでに置き換えられた部分だから検索対象にならない。 よって残っているのは空文字列なので置換演算は終了する。 というわけですね。 whileを使うとうまく行くのは、一回置換するごとに新しい変数として扱われるので また最初から全部検索してくれるため。 というわけですね。 そういう解釈であっていますか?