php:正規表現(preg_replace)を使って、HTMLタグの中以外の単語を別のタグに置き換える処理を教えてください。

今、正規表現を使って、HTML文章中の単語を、別のタグに置き換える処理をPHPにて実装しています。
が、この方法がわかりません。
最初は単純に単語をタグに置き換えると、タグ中に入っていた単語も別のタグに置き換えられてしまいます。

例えば
<img src="xxx" alt="xx単語xx">単語

<img src="xxx" alt="xx<タグ>xx"><タグ>
になってしまいます。
例)

本当は
<img src="xxx" alt="xx単語xx">単語

<img src="xxx" alt="xx単語xx"><タグ>
としたいです。


時間がないため質問させていただきます。
http://q.hatena.ne.jp/1174034774
をみて見よう見まねでやってみたのですが

$html = 'その他の文章など<img src="test.gif" alt="xx単語xx" />xx単語xx';
$word = '単語';
$tag = '<タグ>';
echo preg_replace('/(<("[^"]*"|\'[^\']*\'|[^>])*>'.$word.'?)|'.$word.'/e', '"$1"?str_replace("\\\'","\'","$1"):'.$tag, $html);

Failed evaluating codeが出てしまいました。

回答の条件
  • 1人2回まで
  • 登録:2008/09/04 14:32:18
  • 終了:2008/09/11 14:35:02

回答(5件)

id:pahoo No.1

pahoo回答回数5960ベストアンサー獲得回数6332008/09/04 16:44:32

ポイント20pt

preg系でマルチバイト処理を行うのに不安を感じているものですから、mb_ereg系で書いてみました。正規表現だけで対処しきれなかったので、こんなスクリプトににしてみました。

置換前文字列が $str、置換語文字列が $outstr です。

いちおう複数行対応しています。

$pat1 = "(.*)単語([^>]*<)|(>[^<]*)単語(.*)";
$rep1 = "\\1<タグ>\\2";

while (TRUE) {
    $outstr = mb_ereg_replace($pat1, $rep1, $str);
    if ($outstr == $str)	break;
    $str = $outstr;
}
id:healae

2つ、問題がありました。

①私の環境(PHP5.2.6, UTF8)ではタグ内外ともリプレースされてしまいました。

$str = 'その他の文章など<img src="test.gif" alt="xx単語xx" />xx単語xx';

ちなみにwhileを使わないで一回きりで試してみるとうまくいっています。

②$str = 'その他の文章などxx単語xx';

のように元々にタグがない場合も置換して欲しいです。

すみませんが、調整お願いできますでしょうか。

2008/09/04 17:40:53
id:GoldenDawn No.2

GoldenDawn回答回数426ベストアンサー獲得回数812008/09/04 17:52:56

ポイント20pt
<?php
$str = 'その他の文章など<img src="test.gif" alt="xx単語xx" />xx単語xx';

$target = '単語' ;
$dist   = '<タグ>' ;
echo preg_replace("/(<.*?>)|($target)/e", "'$1' ? '$1' : '$dist';", $str) ;
?>
id:healae

一つ問題がありまして、これをこのまま実行すると

alt=\"のようにダブルクォーテーションの前に"\"がついてしまいました。

調整可能でしょうか。

2008/09/04 18:27:09
id:GoldenDawn No.3

GoldenDawn回答回数426ベストアンサー獲得回数812008/09/04 18:57:49

ポイント20pt
<?php
$str = 'その他の文章など<img src="test.gif" alt="xx単語xx\" />xx単語xx';

$target = '単語' ;
$dist   = '<タグ>' ;
$out = preg_replace("/(<.*?>)|($target)/e", "'$1' ? '$1' : '$dist';", $str) ;
$out = str_replace('\"', '"', $out) ;
echo $out ;
?>
id:healae

とりあえずやりたいことはできました。ありがとうございました。

2008/09/04 20:18:48
id:tezcello No.4

tezcello回答回数460ベストアンサー獲得回数692008/09/04 20:10:17

ポイント20pt

考え方を変えて、全ての置換をしておいて、タグ内についてはもう一度元に戻すように置換するのは?

$html = 'その他の文章など<img src="test.img" alt="xx単語xx" />xx単語xx';
$word = '単語';
$tag = '<タグ>';

$replaced = preg_replace("/$word/u", "$tag", $html);
$replaced = preg_replace("/(<[^>]+?)$tag([^>]+?>)/u", "$1$word$2", $replaced);
id:healae

場合によってはうまくいくのですが

$tagの中にsrc="/img/soccer.gif"のような、スラッシュのついた属性の時

Warning: preg_replace() [function.preg-replace]: Unknown modifier 'g' in

の様にエラーになってしまいます。

私にもう少し正規表現のテクニックがあれば簡単なことなのかもしれませんが、すぐには解決できないので、フォローいただけるとありがたいです。

2008/09/04 23:04:31
id:tezcello No.5

tezcello回答回数460ベストアンサー獲得回数692008/09/05 09:02:23

ポイント20pt

コメント欄が開いていないのでこちらへ。


正規表現中に、デリミタと同じ文字があると失敗します。(今回の場合は/)

他にも正規表現の特殊文字が出て来ると検索に失敗する可能性が出て来ます。

これらをエスケープするのに preg_quote() という関数が使えると思います。

こんな感じでどうでしょう?

http://jp.php.net/manual/ja/function.preg-quote.php

$html = 'その他の文章など<img src="test.img" alt="xx単語xx" />xx単語xx';
$word = '単語';
$tag = '<タグ>';

$replaced = preg_replace('/'.preg_quote($word, '/').'/u', $tag, $html);
$replaced = preg_replace('/(<[^>]+?)'.preg_quote($tag, '/').'([^>]+?>)/u', "$1$word$2", $replaced);

回答数制限ですので、必要ならコメントを開けておいて下さい。

id:healae

ありがとうございます。

ほぼやりたいことができましたが。

$html = 'その他の文章などx単語x単語xx単語x単語x';

のように単語が2回以上出てくると、alt属性の2つめ以降は置換されてしまいます。

対処策あれば……

2008/09/05 12:23:09
  • id:tobeoscontinue
    正規表現(preg_replace)を使ってではないのですがHTMLタグの中と外に分けて、HTMLタグの外(HTMLタグの中以外)についてのみstr_replace()する方法で書いてみました。

    function unhtml_replace($search, $replace, $subject) {
    $split = array_chunk(preg_split("/<(.*?)>/",$subject, -1, PREG_SPLIT_DELIM_CAPTURE),2);
    $subject = '';
    foreach ($split as $tokens)
    $subject .= str_replace($search, $replace, $tokens[0]).
    ($tokens[1] ? '<'.$tokens[1].'>' : '');
    return $subject;
    }

    $html = 'その他の文章など<img src="test.gif" alt="xx単語xx" />xx単語xx';
    $word = '単語';
    $tag = '<タグ>';

    echo unhtml_replace($word, $tag, $html);

    1.preg_split()を使ってHTMLタグの外と中に分けます。
    2.それをarray_chunk()でHTMLタグの外と中のペアーにします。
    3.foreachでHTMLタグの外と中のペアーを一つ取り出し
    4.HTMLタグの外の部分についてstr_replace()で置換します。
    5.HTMLタグの中の文字列は<と>が抜けるので付加して元に戻します。

    一回の処理ではなく数回の繰り返しになるので処理スピード的には不利です。
    utf-8以外ではmbstring(マルチバイト文字)の関数にする必要があるかもしれません。
  • id:healae
    ありがとうございます。
    これはいけるかもしれないので使ってみます。
  • id:tezcello
    > $html = 'その他の文章など<img alt="x単語x単語x" src="./test.img">x単語x単語x'
    alt 属性値として、同じ単語が複数回あるのはおかしいとは思いますが、対策出来ない事は無いようです。
    元の文字へ戻す置換(2回目の preg_replace())の部分を
    $c=1;
    while($c){
    $replaced = preg_replace('/(<[^>]+?)'.preg_quote($tag, '/').'([^>]+?>)/u', "$1$word$2", $replaced, -1, $c);
    }

    こんなようにすると出来ると思います。お試しください。
    ただし、alt 属性値に置換したい文字列の最大数(例の場合は2)+1回だけ頭からスキャンしなおすでしょうから多少時間が掛かりますね。

この質問への反応(ブックマークコメント)

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

絞り込み :
はてなココの「ともだち」を表示します。
回答リクエストを送信したユーザーはいません