2012-05-11 12 views
5

私がやろうとしていることは、かなり大きいPHPファイルを検索し、その中のどこかに "search_term"という文字列を含むPHPコードブロックを置き換えることですいくつかの他のコードで。私。は、検索用語(perl正規表現)に最も近い文字列を一致させます

<?php 
//some stuff 
?> 
<?php 
// some more stuff 
$str = "search_term"; 
// yes... 
?> 
<?php 
// last stuff 
?> 

私がこれまで持っているもの

<?php 
//some stuff 
?> 
HELLO 
<?php 
// last stuff 
?> 

になる必要がありますが代わりに、

$string =~ s/<\?php(.*?)search_term(.*?)\?>/HELLO/ims; 

これは正しく最も近い決算?>と一致しているが、非常に最初の<?phpで試合を開始します文字列に最も近いもののsearch_term

私は間違っていますか?

答えて

5

一般的に、これは通常、このような問題につながるため、貪欲でないマッチングを使用するのは嫌です。 Perlはあなたのファイルを見て、最初に'<?php'を見つけ、残りの正規表現を探し始めます。最初の'?>'と2番目の'<?php'は、.*と一致しているため、search_termと次の'?>'が見つかり、終了しています。

貪欲でない一致とは、本当に必要なものよりも多くのものにマッチする正規表現を持つことを意味し、どのマッチを返すかを決めるためにperlに任せます。マッチさせたいものと正確に一致する正規表現を使用する方が良いでしょう。この場合、あなたは((?!\?>).)*代わりの.*?を使用することによって、あなたが欲しいものを得ることができます((?!\?>)が負の先読みアサーションです)

s/<\?php((?!\?>).)*search_term((?!\?>).)*\?>/HELLO/is; 

あなたが複数の一致を期待する場合は、むしろ/isよりも/isg使用する場合があります。文字が?>またはsearch_termの始まりではないことを確認すること後、一度

@blocks = split /(\?>)/, $string; 
while (@blocks) { 
    $block = shift @blocks; 
    $sep = shift @blocks; 
    if ($block=~/search_term/) { 
     print "HELLO"; 
    } else { 
     print $block, $sep; 
    } 
} 
+0

ありがとうございます。私の特定のシナリオではブロックの事は実際には理想的でした – Mala

2

最初のキャプチャグループを置き換えるだけで済みます。このような何か:

s/<\?php(.*)<\?php(.*?)search_term(.*?)\?>/<\?php$1HELLO/ims 
+0

ちょうどこれを試しました...それは 'search_term'の前に部品を取り除かない – Mala

+0

hooray!それは次のように働いています: '/ <\?php(。*)<?php(。*?)search_term(。*?)\?>/<\?php $ 1HELLO/ims' – Mala

+0

ああ、ok編集後世のために。 – Benj

0

あなたは貪欲けちマッチングを使用しているが、それはまだあまりにも多くを一致させることができます。

Matching repetitions in perlretutはそれをよく表しています。

私は助けに否定一致を使用することがありますが、助けになるとは思わないでしょう。例:

s/^[^A]*A/A/ 

文字が一致しないことを確認します。

しかし、私は通常、複数の行にまたがっているのではなく、私がしなければperlを使用していません。あなたの正規表現で

+0

Ermどこですか? '。*?'は非貪欲です。 – Benj

+0

真。私は間違っていますが、必要以上に一致しています。 – Julian

1
s/(.*)<\?php.*?search_term.*?\?>/${1}HELLO/ims; 

、正規表現エンジンは、あなたのターゲット式にマッチする部分文字列の初期発生を見つけようとしている、そしてそれは?><?php第一及び第二の間でそれを見つけました。正規表現の先頭に(.*)を置くことによって、あなたは(.*は、文字列全体にマッチするので)文字列の末尾に行き、その後、それが文字列を見つけることができるスポットにバックトラックに正規表現エンジンをだまし

<?php」 。そうすれば結果として得られる一致には、必要以上にトークンが含まれなくなります。<?php

+0

**もしあなたがただ一つのコードブロックを置き換えたいなら、これは@ Benj'sより良い解決策になるでしょう。しかし、それは私がその質問をどのように読むのかではありません。 –

2
$string =~ s/<\?php(?:(?!\?>|search_term).)*search_term.*?\?>/HELLO/isg; 

(?:(?!\?>|search_term).)*マッチ1つの文字:

また、わずか数ブロックにファイルを分割します。それが一致しなくなったときに、文字列の次のものがsearch_termであれば、それを消費し、それ以降はすべて?>まで消費します。そうでない場合、その試みは失敗し、次の<?phpで開始されます。

@ RobertYoungのソリューションと同様に、search_termを検索すると、?>と一致することは許されません。 search_termと一致しないと、バックトラッキングがなくなり、検索がより効率的になります。重要ではないソース文字列のサイズにもよりますが、パフォーマンスを著しく損なうことはありません。

@ Benjのソリューション(現在掲載中)は機能しません。それは、あなたが提供したサンプル文字列で、望ましい出力をもたらしますが、それは偶然に過ぎません。これはの最後のコードブロックをsearch_termに置き換え、(@mobコメントとして)それは最初のコードブロックの内容を完全に無視します。

関連する問題