Perlで複数行の置き換えは?

[上に] [前に] [次に]
J.Naka [E-Mail] 1999/06/05(土) 15:20:48
Perlで複数行の置き換え(=削除)が旨くいきません。
下のコードはテーブル全てを削除するコードのつもりです。
動作修飾子の /gs を /gs とか /gsmとかしてもダメでした。

$x =~ s/^<table width=500>.*<br>$/\n\n<!-- Kill -->\n\n/gs;

参考文献:
とほほWWW
「CGI&Perl」ポケットリファレンス 技術評論社

J.Naka [E-Mail] 1999/06/05(土) 16:24:37
検索パターンのメタ文字 .* を、.*? とすると
置き換え出来ました。
しかし、なぜそのようなメタ文字の組み合わせに
なるか分からない(^^?

じぇい 1999/06/05(土) 21:34:45
どういう処理なのかいまいちよくわかりませんが、.*?というのはPerl5で使える最小マッチの方法です。
例えば、

$hoge = "<table>test<br><table>test2<br>";

$hoge =~ s/^<table>.*?<br>//ig;

とした場合は、$hogeはたった一回しかマッチしません。それは、そういう仕様で、Perlは.*と書いた場合、できるだけ長い文字列をマッチをさせようとするからです。
それで、それでは困る場合に.*?と記述すれば、できるだけ短い文字列のマッチ(最小マッチ)を行うのです。

、、、というか、、そういうことではないのでしょうか?

じぇい 1999/06/05(土) 21:38:17
すいません。記述ミスしました。

$hoge = "<table>test<br><table>test2<br>";

$hoge =~ s/^<table>.*<br>//ig;

と書いた場合は、

ここから→<table>test<br><table>test2<br>←ここまでマッチします。
しかし、

$hoge = "<table>test<br><table>test2<br>";

$hoge =~ s/^<table>.*?<br>//ig;

こうした場合は、

ここから→<table>test<br>←ここまで
ここから→<table>test2<br>←ここまで

この二つがマッチするのです。

羽崎 1999/06/06(日) 01:12:53
ちょっと強引な気もしますが・・・(^^;
改行を含む場合は
#------------

$htm =<<HTM;
<HTML><body>
<table>
 <tr bgcolor="red">
 <td width="40">test1</td>
 <td width="80">test2</td>
 </tr>
 <tr bgcolor="blue">
 <td width="40">test3</td>
 <td width="80">test4</td> </tr>
</table>
<BR>
----------
<table> 
 <tr bgcolor="red">
 <td width="40">test1</td>
 <td width="80">test2</td>
 </tr>
 <tr bgcolor="blue">
 <td width="40">test3</td>
 <td width="80">test4</td>
 </tr>
</table>
<BR>
</body></HTML>
HTM
$htm =~ s/<table[\w\W]*?<br>/\n\n<!-- Kill -->\n\n/gi;
print $htm;

#------------

とか。あんまりスマートじゃないかも?
#どういう意味で^(文字列の先頭)と$(文字列の末尾)を付けている
#のかわからなかったので無視しました。

想像ですが、*単独の場合復帰的にマッチさせているんじゃないで
しょうか?んで、*?の場合は復帰無くただ一度だけマッチさせる
って意味のような・・?
うーん、B-Cusさんあたりならわかるかも?(←他人任せ^^;)

じぇい 1999/06/06(日) 04:04:36
すいません。まず謝っておきます。
ピントがずれてた上にでたらめ書いてました。

$hoge = "<table>test<br><table>test2<br>";

$hoge =~ s/^<table>.*?<br>//ig;

これで2回マッチするなんて書きましたけど、、これでも1回しかマッチしませんね。 ^  なんて余計なものつけてました。

しかも、、質問の内容とぜんぜん違った回答してましたね…。

J.Naka [E-Mail] [HomePage] 1999/06/06(日) 22:30:50
ども、じぇいさん 羽崎さん。
まず、自分はPerlを遣り始めて一週間程度という事です。
「CGI&Perl」ポケットリファレンスも二日前に
購入したところです。ですから知識としては拾い読み程
度の抜けだらけの知識です。


Re:じぇいさん
 最小マッチを知りませんでした。(^^;
「CGI&Perl」ポケットリファレンスでみると、
最長マッチと最短マッチの記述あります。
じぇいさんの云う「最小マッチ」とは最短マッチのことのようです。
Perlはデフォルトでは最長マッチを行い、量指定子(メタ文字?)
の直後に ? を置くと最小マッチするということらしいです。
 ついでに[P135]を引用します。
------------------
量指定子は、その前に置かれた文字などのアトムを繰り返すパターンを表します。
くり返し回数が指定される{n}を除き、通常量指定子は最長マッチを行います。
つまり、文字列中でマッチするものの中で最も末尾に近いものにマッチします。
量指定子の直後に ? を置くと、最短マッチを行うようになります。つまり、
文字列中でマッチするものの中で最も先頭に近いものにマッチするようにな
ります。
------------------
という事で、
「最長マッチ」はマッチする最長。「最短マッチ」は最も短いマッチ。
というように用語をストレートに解釈するのとは少し意味が違うよ
うです。(^^;やっややこしいぞー(笑)


>これで2回マッチするなんて書きましたけど、、これでも1回しかマッチしませんね。
> ^  なんて余計なものつけてました。
いや最短マッチを二回してると言えると思います。

>しかも、、質問の内容とぜんぜん違った回答してましたね…。
飛んでも御座いません。疑問のあるときは色々なアプローチが理解のヒントになります。

#C/C++のポインタ演算に劣らず負けずメタ文字は使用シチュ
 エーションでその意味が違ってくるのですね。。。ややこしいぞ(^^;

Re:羽崎さん
>#どういう意味で^(文字列の先頭)と$(文字列の末尾)を付けている
>#のかわからなかったので無視しました。
これは、「CGI&Perl」ポケットリファレンスの[P144]に、
------------------
●m修飾子
 パターンが複数行になっている場合、正規表現の ^ は文字列の先頭以外に改
 行文字の直後にマッチするようになり、正規表現の $ は文字列の末尾以外に
 改行文字の直前にマッチするようになります。
------------------
との事なので、これは、
 パターンが複数行の場合、^ は、複数行の先頭のみでなく、途中
行の先頭にもマッチすると解釈してるんですが、動作的になんか変
なのですよ(笑)

 ここでいう、複数行パターンとは、検索される未知の文字列では
なく、それに対する既知の検索文字列の事で、自分はそれを反対に
取り違えていたようです(笑)(^^;

■総論。。。おっナマイキ(^^;
 総じて、ポイントはじぇいさんの行っておられる事が一番的に近いと思いますが、
「最長マッチ」「最短マッチ」がその言葉通りの動作では無いのがミソのようです。
今回は自分にとってかなり勉強になりました。有り難う御座いますぅ(_ _)

B-Cus 1999/06/07(月) 00:48:32
> 「最長マッチ」はマッチする最長。「最短マッチ」は最も短いマッチ。
> というように用語をストレートに解釈するのとは少し意味が違うよ
> うです。(^^;やっややこしいぞー(笑)
いや、最長/最短という言葉通りの解釈でいいんじゃないかしら。
最長であろうが最短であろうが、マッチする起点は変わらないわけですから。

 「つまり、文字列中でマッチするものの中で最も末尾に近いものにマッチします。」
 「つまり、文字列中でマッチするものの中で最も先頭に近いものにマッチするようになります。」
これってわかりにくいというか、間違っているというか…。

羽崎 1999/06/07(月) 03:01:43
>J.Nakaさん
私、理解が悪いのでいくつか疑問点を・・・

まず、私のやり方ではなにか問題があったんでしょうか?(^^;
方法論として、J.Nakaさんの目指していた方法と違ったのででしょうか?

>●m修飾子
これについてはらくだ本(Perl5を未サポートのPerlリファレンス)
には載っていなかったので(←たぶん)私は知りませんでした。
J.Nakaさん言うのとおりに解釈するとたとえば次のコードは

#============

$test="aaaaabbbbb\naaabbb\naaabbb\n";
$test =~ s/^a*b*$/ooo/m;
print $test;
#============

ooo
ooo
ooo

と出力されると想像しましたが実際は

ooo
aaabbb
aaabbb

と出力されました。
そこで、おそらくgオプションをつけない場合、マッチするのは
ただの一回のみと考えます。
そこでgオプションをつけてみました。(mg)

ooo
ooo
ooo

と出力しました。
さらにmオプションをとると(g)

aaaaabbbbb
aaabbb
aaabbb

と出力しました。(マッチしていない)

以上から、途中行にもマッチしています。
重要なのは、mオプションのときは改行文字\nによって文字列を分けて
解釈している点です。上の例でmオプションがない場合は\nのために
置換ができていません。

こういう意味でいいでしょうか?

>いや、最長/最短という言葉通りの解釈でいいんじゃないかしら。
はい。マッチする起点から最短か最長かって意味でしょうね。

$test = "aaabaaab";

のとき

$test =~ s/a\w*b/ooo/g;
なら
ooo

$test =~ s/a\w*?b/ooo/g;
なら
oooooo

となるはず。

私も今回はマッチについて、勉強になりました。:-)

J.Naka [E-Mail] 1999/06/10(木) 20:34:32
[[解決]]
おっとと、随分遅い最終書き込みになってしまいました。(笑)
さて、自分の混乱の原因は、B-Cus指摘の部分ですね。
書籍の記述がまずいです。
早速出版社に文句出します。#方法が葉書きにしてって(笑)。

書籍から自分の解釈では、
 最長マッチとは文字列中の最も長いマッチで
 基点はマッチ毎に不定。
という物でしたが、これは間違いで、最初にマッチした
箇所の基点が以降のマッチの基点となる。
という解釈が正解のようですね。
#(^^;どうか違ってないように(笑)

J.Naka [E-Mail] 1999/06/10(木) 20:44:54
>て、自分の混乱の原因は、B-Cus指摘の部分ですね。
平に訂正。
て、自分の混乱の原因は、B-Cusさん指摘の部分ですね。
I am sory.

mm 1999/06/10(木) 23:01:10
>さて、自分の混乱の原因は、B-Cusさん指摘の部分ですね。
>書籍の記述がまずいです。
本当に書籍の記述がまずいのでしょうか?
「CGI&Perl」ポケットリファレンスという本を持ってる訳ではない
のですが、J.Naka さんが引用されている個所は、量指定子の部分ですよね。
その前の方に、最も左寄り(最左)でマッチするという記述はありませんか?
perlの正規表現のマッチは、最も左寄りが大前提になります。これに対して、
最長は、二義的な原則です。例えば、この左寄りの原則は、| を超えて適用
されますが、最長の原則は、| を超えません。
従って、最も左寄りでマッチするという前提で、先の引用文を読むなら、
「文字列中でマッチするものの中で」という表現には一考の余地はありそう
ですが、必ずしも間違いとは言いきれないと思うのです。
技術評論社には何の縁もないのですが、このままではダメ本の烙印が押されて
しまいそうなので、ちょっとでしゃばらせて頂きました。
もし、最も左寄りでマッチするという記述がないなら、お詫び致します
(もしそうなら、そんな本は捨ててしまってもいいと思います…)。

[上に] [前に] [次に]