OKWAVEのAI「あい」が美容・健康の悩みに最適な回答をご提案!
-PR-
解決
済み

置換演算子についての疑問

  • 困ってます
  • 質問No.160538
  • 閲覧数141
  • ありがとう数3
  • 気になる数0
  • 回答数3
  • コメント数0

お礼率 72% (83/114)

お世話になっております。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
  • 気になる
    質問をブックマークします。
    マイページでまとめて確認できます。

質問者が選んだベストアンサー

  • 回答No.1

 えとですね。
 正規表現には1つの原則がありまして。

 たとえば、123456789だと、可能性として、

  1,23456789
  12,3456789
  123,456789
  1234,56789
  12345,6789
  123456,789

 これらすべて、マッチする可能性がありますよね。
 そういうパターンであることはわかりますか?

 で、正規表現は、「マッチする可能性の中から、もっとも文字列の長いものを採用する」という原則があります。
 ですので、.* にひっかかる可能性のあるうちで、もっとも長い文字列である 12345 がひっかかってるわけです。

 これは正規表現すべてで統一されていて、最長マッチといいます。
 逆に、もっとも短い可能性を採用させるには、

  (.*?\d)(\d\d\d)

 と、* のあとに ? を記述します。
 もっとも、これをやってもおっしゃったような状況にはならず、

  1,2345,6789

 になりますが。
 (これは、一度置き換えの対象になった部分は二度と検索しないという規則があるからです)
補足コメント
horagai

お礼率 72% (83/114)

な~るほど。質問の件についてはよーくわかりました。

>これらすべて、マッチする可能性がありますよね。
>そういうパターンであることはわかりますか?
もちろんわかります。だから置換演算子が前からマッチさせていくとすると
うまく行く理由がわからなかったのです。

>「マッチする可能性の中から、もっとも文字列の長いものを採用する」
>という原則があります。
この原則は知りませんでした。とにかく前から1文字ずつ読んでいって、マッチする
パターンが最初に出現した時点で即置換するものだとばかり思っていましたが、
一度最後まで読んでからパターン検出をするのですね。

ですがこの原則はあくまで長さが不定のパターンに対する原則ですよね。
長さが一定になるようなパターンなら、例えば(\d\d)(\d\d\d)などであれば
やはりパターンが最初に出現した部分にマッチすると考えてよいのでしょうか?
また正規表現の上では長さ不定でも、たまたま与えられた文字列ではそのパターンに
マッチするすべての場所が同じ長さになってしまった場合はどうなるのでしょう。
その場合も最初に出現した部分にマッチするのでしょうか?

>一度置き換えの対象になった部分は二度と検索しないという規則があるからです
なるほど。それで(2)の疑問、すなわちgオプションを付けても一度しか置換して
くれない理由もわかりました。
最初に
「マッチする可能性の中から、もっとも文字列の長いものを採用する」
という原則にしたがって (12345)(678) でマッチさせ (12345),(678)とする。
すると (12345,678)はすでに置き換えられた部分だから検索対象にならない。
よって残っているのは空文字列なので置換演算は終了する。
というわけですね。
whileを使うとうまく行くのは、一回置換するごとに新しい変数として扱われるので
また最初から全部検索してくれるため。
というわけですね。
そういう解釈であっていますか?
投稿日時 - 2001-11-01 13:13:57
-PR-
-PR-

その他の回答 (全2件)

  • 回答No.2
レベル12

ベストアンサー率 75% (398/526)

最長マッチについては、deagleさんのアドバイスでご理解頂けていると思います。 $num="1234567890"とすると、1回目のマッチングでは「1234567,890」となりますね。 s///は「置換した回数」(この場合1)を返すので、置換が成功するとwhileの条件が真となり、また置換を行おうとします。 この繰り返しで、2回目で「1234,567,890」、3回目で「 ...続きを読む
最長マッチについては、deagleさんのアドバイスでご理解頂けていると思います。

$num="1234567890"とすると、1回目のマッチングでは「1234567,890」となりますね。
s///は「置換した回数」(この場合1)を返すので、置換が成功するとwhileの条件が真となり、また置換を行おうとします。
この繰り返しで、2回目で「1234,567,890」、3回目で「1,234,567,890」となり、ここでマッチしなくなるためs///が0を返し、whileが終了します。

下記HPが大変参考になりますので、ご一読ください。
お礼コメント
horagai

お礼率 72% (83/114)

ご回答ありがとうございます。
御紹介いただいたURLは知っていたのですが、「数字をコンマで区切る」
の項目には気がつきませんでした。たしかに参考になりました。
正規表現は奥が深いですね。
投稿日時 - 2001-11-01 15:38:59


  • 回答No.3

 leaz024さんに補足していただいているようですが念のため。 >長さが一定になるようなパターンなら  はい。  実際に試してみました。必ず先頭からです。  最後に $ を付けたら最後からになるかと思ったんですが、やってみたらやっぱり先頭からでした。  ですので、正規表現の検索は必ず先頭からってことになるようです。 >そういう解釈であっていますか?  これも合ってます。はい。 ...続きを読む
 leaz024さんに補足していただいているようですが念のため。

>長さが一定になるようなパターンなら
 はい。
 実際に試してみました。必ず先頭からです。
 最後に $ を付けたら最後からになるかと思ったんですが、やってみたらやっぱり先頭からでした。
 ですので、正規表現の検索は必ず先頭からってことになるようです。

>そういう解釈であっていますか?
 これも合ってます。はい。
お礼コメント
horagai

お礼率 72% (83/114)

いろいろとありがとうございました。
いままで掲示板ソフトなどで置換演算子を使ってみてもなかなか思い通りの
置換をしてくれず、その理由がわからなくて悩んでいたのですが
「マッチする可能性の中から、もっとも文字列の長いものを採用する」
「一度置き換えの対象になった部分は二度と検索しない」
の2つの原則を知っただけで置換演算子の振舞の謎はほとんど解けました。
おかげさまでこれからは悩むことも減りそうです。
また何かありましたら宜しくお願いします。
投稿日時 - 2001-11-01 15:19:32
このQ&Aで解決しましたか?
関連するQ&A
-PR-
-PR-
こんな書き方もあるよ!この情報は知ってる?あなたの知識を教えて!
このQ&Aにはまだコメントがありません。
あなたの思ったこと、知っていることをここにコメントしてみましょう。

その他の関連するQ&A、テーマをキーワードで探す

キーワードでQ&A、テーマを検索する
-PR-
-PR-
-PR-

特集


いま みんなが気になるQ&A

関連するQ&A

-PR-

ピックアップ

-PR-
ページ先頭へ