ログインしてさらにmixiを楽しもう

コメントを投稿して情報交換!
更新通知を受け取って、最新情報をゲット!

超初心者向けCGI講座コミュの[質問] 正規表現の「置換」の書き方

  • mixiチェック
  • このエントリーをはてなブックマークに追加
はじめまして。Perl&正規表現に関して初心者ですが、現在使用中の掲示板CGIの検索機能を少し改造し、「検索結果を表示した各記事の文中で検索キーワードを赤字で強調表示させる」事ができないか・・・と考えております。

[行いたい事]
検索中に使われたキーワード:$word
表示する文書:$com

・・・であれば、$comに含まれる$wordを <b>$word</b>に置換したい・・・という事なのですが、可能でしょうか?

[試してみた事]
検索結果を1行毎にHTMLに書き込みを行う直前に以下を書いてみた。
$com =~ s/\$word/<b>\$word<\/b>/g;

[結果]
表示に変化なし。(ソースを見ても、置換されていないようです。)

正規表現の置換で、スカラー変数を入れる事はできないのでしょうか?それとも書き方次第でどうにかなるものなのでしょうか?

ご存知の方、どうぞ宜しくお願いいたします。m(__)m

コメント(21)

追記:

すみません、トピックに「赤字で強調表示」と書きましたが、記述例は、「太字」ですね。(^^ゞ
いずれにしてもうまく行ってません。
宜しくお願いいたします。
ジェミーさん、こんにちは。

 この間、そんな検索ワードだけを赤くするのを作りました。

if($com =~ /$word/){
$_ =~ s/$word/<font style=?"color:#ee4e3e; font-weight:bold;?">$word<?/font>/g;

 で、できました。
 上手くフェミーさんのに合うことを願っています。
 すみません、記述が同じというオタコなレスをしてしまいました。

 同じように表示に変化しない事があり、
$com =~ s/?$word/<b>?$word<?/b>/g;
 の時点で、$wordに何も入っていなかったことがありました。
$com =~ s/\$word/<b>\$word<\/b>/g;
の\記号を外して実験してみました。

--- 実験スクリプト ---
my $word = "アイウエオ";
my $com = "あいうえおアイウエオあいうえおアイウエオあいうえお\n";

print $com;
$com =~ s/$word/<b>$word<\/b>/g;
print $com;

--- これを実行した結果 ---
あいうえおアイウエオあいうえおアイウエオあいうえお
あいうえお<b>アイウエオ</b>あいうえお<b>アイウエオ</b>あいうえお
はじめまして。

正規表現の置換処理にスカラー変数を入れることはできますが、「$word」の前に「\」を付けてしまうと「$」がエスケープされてしまうので変数として展開されず、まさに「$word」という検索文字列しか置換されなくなります。

ちなみに、「?」は「直前のパターンが0回または1回にマッチ」するので、「?」の前になにもなければ(直前のパターンがなければ)とくに効果はなく、置換後文字列に記述しても効果はありませんです・・・。

従って、れいさんのスクリプトが正解になりますね。置換文字列内にHTMLタグやスラッシュが含まれる場合は、スラッシュで区切らずに、

 $com =~ s|$word|<b>$word</b>|g;

などのように書くと、スラッシュをエスケープしなくてもよいので、可読性が高くなりますよ。厳密に言えば、文字コードの処理や大文字・小文字・空白の置換なども必要になるでしょうが、その辺りはお好みでどうぞ^^
ボッキ〜中島さん、れいさん、いろいろご親切に教えていただき、ありがとうございました!

結果からご報告いたしますと、

「うまく行きました!\(^_^)/」

まず、ポッキー中島さんの助言により、

> の時点で、$wordに何も入っていなかったことがありました。

printで確認してみましたら、確かに何も入ってませんでした。(^^ゞ
(挿入しようとした箇所が、後ろすぎたようです。)
そこで、もう少し前の位置でに式を移動し、

さらに、れいさんのご助言のように

> の\記号を外して実験してみました。

\の記号をはずしてみたら、うまく行きました!
$マークをエスケープする必要はなかったのですね!!!
さらに応用してAND検索でも全てのワードがマーキングされるように仕上げる事ができました。

お2名様のすばやいコメントに感謝感謝です!
お世話になりました。今後ともどうぞ宜しくお願いいたします。
keiさん、大変詳しいコメント、ありがとうございました!

また、可視性がよくなる記述方式も、大変勉強になります。
確かに、スラッシュが多いと、どこが区切りなのかわからなくなりますものね。

#6でご報告しましたように、お陰様でうまく行きました。
ありがとうございました。m(__)m
ジェミーさん、動いて良かったですね。

 私のはアドバイスじゃなくって、無限なる失敗例からです。
 ジェミーさんの $wordの前のエスケープのバックスラッシュを見逃していましたです。ペコリ。

#3の「$com =~ s/?$word/<b>?$word<?/b>/g; 」の「?」は、クエッションマークではなく、バックスラッシュだったのですが、何故かミクシーでは文字化けとしてクエッションマークが表示されてしまいます。
 MacOS X + Jedit4 だからでしょうか、原因不明です。
 毎度ながらお騒がせいたしました。
ボッキ〜中島さん

なるほどー、Macからの書き込みですと、?に化けちゃうのですね。
今後、中島さんからコメントいただく場合には、その点に注意しながら、読むようにしたいと思います。(^^)
ご説明ありがとうございます。
すみません、皆様のアドバイスを元に、目的達成\(^_^)/・・・と思っていたのですが、一部動作がうまく行っていない事に気がつきました。(涙)

改造を行っているのは、KENT-WEBさんのYY-BOARD(yybbs.cgi)最新バージョンでして、以下のように1行だけ追加しています。
この1行を追加した事により、検索結果画面表示では、検索キーワードが赤太字で強調表示されておりました。
(OR検索の場合は、最初にヒットしたキーワードのみが強調されますが、「全く強調されないよりはマシ」と、それは割り切りとしました。)

ところが・・・ところが・・・後から気がついたのですが、

1.この1行を追加した事により、検索キーワードとして「バー」や「ビー」を入力すると、処理が止まってしまい、結果表示が全くされないという問題が発生します。
(0件であっても、「検索結果:0件」と表示されるのが期待される動作です。)
単独のキーワードで入力した場合でも、スペース区切りで複数単語のひとつとして入力した場合でも、問題が発生します。

2.「カフェバー」や「レストランバー」と入力すると、問題は発生しません。

3.追加した1行を削除すると、「バー」や「ビー」と入力しても、問題は発生しません。

4.単独のCGIで、
-----------------------------------
my $word = "バー";
my $com = "あいうえおアイウエオあいうえおバーアイウエオあいうえお\n";

print $com;
$com =~ s/$word/<b>$word<\/b>/g;
print $com;
-----------------------------------
  を試すと、期待通りの動作になりました。

5.$_ =~ s/$wd/<font color=red><b>$wd<\/b><\/font>/g; と書いても結果は一緒でした。


つまり・・・今回追加した1行が、何か処理に悪影響を与えてしまっているようなのです。
(発生条件は、よくわかっておりませんが。)


何か、思い当たる事はございますでしょうか?m(__)m

※MIXIの掲示板は、連続した半角スペースやタブを削除するようで、全行が左寄せになっちゃってます。m(__)m
みづらくてすみません。
#-------------------------------------------------
# 検索処理
#-------------------------------------------------
sub search {
local($file, $word, $view, $cond, $job) = @_;

# キーワードを配列化
$word =~ s/\x81\x40/ /g;
my @wd = split(/\s+/, $word);

# ファイル展開
print "<dl>\n";
my $i = 0;
open(IN,"$file") || &error("Open Error: $file");
my $top = <IN> if ($job ne "past");
while (<IN>) {
my $flg;
foreach my $wd (@wd) {
if (index($_,$wd) >= 0) {
$flg++;
$_ =~ s|$wd|<font color=red><b>$wd</b></font>|g; # ← ■■ここに1行追加■■
if ($cond eq 'OR') { last; }
} else {
if ($cond eq 'AND') { $flg = 0; last; }
}
}

# ヒットした場合
if ($flg) {
$i++;
next if ($i < $page + 1);
next if ($i > $page + $view);

my ($no,$reno,$dat,$nam,$eml,$sub,$com,$url,$hos,$pw,$col,$ico) = split(/<>/);
if ($eml) { $nam = "<a href=\"mailto:$eml\">$nam</a>"; }
if ($url) { $url = "<<a href=\"$url\" target=\"_blank\">Home</a>>"; }

# 結果を表示
print "<dt><hr>[<b>$no</b>] <b style=\"color:$subcol\">$sub</b> ";
print "投稿者:<b>$nam</b> 投稿日:$dat $url<br><br>\n";
print "<dd style=\"color:$col\">$com\n";
}
}
close(IN);

print <<EOM;
<dt><hr>
検索結果:<b>$i</b>件
</dl>
EOM

my $next = $page + $view;
my $back = $page - $view;
return ($i, $next, $back);
}

宜しくお願いいたします。
同様の記述を施したyybbs.cgiで動作確認してみましたが、症状を再現できませんでした。

「処理が止まってしまい、結果表示が全くされない」とは、具体的にどのような状態(サーバーエラーが表示される・画面にまったくなにも表示されないなど)でしょうか?

また、他に改造している点があるかないか、ジェミーさんの動作環境(サーバーOSやブラウザなど)の情報があると、原因を特定しやすくなると思います。
ちなみにですが、

> OR検索の場合は、最初にヒットしたキーワードのみが強調されますが、
> 「全く強調されないよりはマシ」と、それは割り切りとしました。

こちらについては、以下のようにすれば解決できます。


foreach my $wd (@wd) {
 if (index($_,$wd) >= 0) {
  $flg++;
  $_ =~ s|$wd|<font color=red><b>$wd</b></font>|g;
#  if ($cond eq 'OR') { last; } # ■■この行をコメントアウト■■
 } else {
  if ($cond eq 'AND') { $flg = 0; last; }
 }
}
Keiさん、ご親切にいろいろと確認をいただき、ありがとうございます!m(__)m

> 同様の記述を施したyybbs.cgiで動作確認してみましたが、症状を再現できませんでした。

そうですか。
こちらでは、原因の切り分けをする為に、再度yybbs.cgiをオリジナルの設定のまま(変更したのはパスワードのみ)で、設置し、ログファイルのみ現状使っているものを読み込ませました。

何もしない状態 → 検索OK
「$_ =~ s|$wd|<font color=red><b>$wd</b></font>|g; 」を追加 → 検索NG

になりました。

発生条件の絞込みは相変わらずできておりませんが、少なくとも

1.カタカナの一部で発生
2.そのキーワードがどこかの記事に含まれている時に発生

しているようです。2番目の条件は、後から気がつきました。

現象としては、キーワードを入力し、検索開始すると、再度検索画面全体の書き出し開始しますが、送信キーが表示されたところで止まります。(そこから下は真っ白)
つまり、本当に検索処理を行っているところで止まっているようで、恐らくは文字の置換のところで止まっているのではないかと思っております。

サーバーは、「さくらのレンタルサーバー」で、環境は
OS Version FreeBSD 4.10-RELEASE-p24r1 i386
Apache Version Apache/1.3.37 (Unix)
Perl 5.8 /usr/bin/perl 5.8.4

クライアントPCは、WinXPで、ブラウザはMSIE6.xです。

現在現象が発生しているテスト環境のURLは、こちらの掲示板に書き込む事はできませんが、メッセージでご連絡する事はできます。(ご許可をいただければ・・・ですが。)

何かわかりましたら・・・宜しくお願いいたします。m(__)m
#12への返信です。

Keiさん、こちらも親身なアドバイス、ありがとうございます!

試してみましたら・・・こちらはうまく行きました!(NGにならないキーワードでは、OR検索でも、全てのキーワードが赤くなりました!)

感謝感謝です!m(__)m

ただし、#10に記述した問題点は、残念ながら残存しております。(涙)
問題の発生条件がさらに絞り込めました。

「ー」を含む用語で、

且つ、一致(部分一致も含む)する場合 → 検索異常で終わる
且つ、一致しない場合 → 「検索0件」で正常に終わる

たとえば、どこかの記事に「リバーサイド」というキーワードが含まれている場合、

「リバー」「バー」「ー」は、いずれも検索異常(検索結果表示なしのまま)で終わります。
「リバーカフェ」「リバーサイドカフェ」は「検索0件」で正常に終わります。

これで、何か原因がわかりますでしょうか?m(__)m
tateisuさん、アドバイスありがとうございました!

tateisuさんのアドバイスをヒントに

s|\Q$wd\E|<font color=red><b>$wd</b></font>|g;

にして、該当個所に挿入してみたら・・・・問題が解決しました!($1のままでは、変換された言葉が消えてしまいました。)
\(^_^)/\(^_^)/\(^_^)/
#16で書いていた現象も消えました。

今回質問させていただいたく事により、Keiさんのお陰でOR検索の検索表示も完璧に赤くする事ができましたし・・・パソコンの前で小躍り状態でございます!

Keiさん、taitesuさん、本当にありがとうございました!m(__)m

今回、何故\Qと\Eで囲わなければ駄目だったのかが、まだよくわかっていない(何故左側だけ?)のですが、もし宜しければ教えていただけますでしょうか?

宜しくお願いいたします。
すみません、思い出しました。文字コードShift_JIS特有の(ある意味で有名な)問題ですね。

文字コードShift_JISの長音記号「ー」は1バイト目が「0x81」、2バイト目が「0x5B」なのですが、「0x5B」は「[」(ブラケット)という記号と解釈されます。

「[」は正規表現の文字集合を示すメタ文字なので、相対する「]」もないと、正規表現が正しくないということでエラーになります。

「\Q〜\E」は、「\Q」から「\E」までのメタ文字をエスケープするという意味になります。結果として「[」がエスケープされる為にエラーになりません。

理想的なのは、スクリプトをEUC化して、フォームから入力された文字コードもEUCにエンコードしてから処理することですね。

↓↓詳しくは、こちらをどうぞ↓↓
http://www.din.or.jp/~ohzaki/perl.htm#JP_EUC_JP
ちなみにですが、おそらくtateisuさんがやりたかったのは、

s|(\Q$wd\E)|<font color=red><b>\1</b></font>|g;

だと思われます。

これは、正規表現内で1番目の文字グループ(()で囲まれたパターン)にマッチした文字列を「\1」で参照するという意味になります。正規表現の外でマッチした文字列を参照するには「$1」になります。
Keiさん、詳しいご説明、ありがとうございました。
なるほど、本文中のShift-JISの文字に原因があったのですね。大変勉強になりました。

ご紹介いただいたURLの内容も読みました。
確かにスクリプト修正前の環境では「ー」のみならず、「能」や「表」でも検索異常が発生しうる状態であった事を、先ほど確認できました。

今のところメタ文字をエスケープ後は、検索異常は見つかっておりませんので、しばらくはShift-JISのまま運用させていただこうと思ってます。

それから、#20での補足のご説明、ありがとうございます。
別のところ(日時情報の抽出方法)で、Keiさんにグループパターンについて教えていただいたばかりでしたが、正規表現内では\1と表現されるのですね。これまた勉強になりました。

本当にありがとうございました。m(__)m

ログインすると、残り1件のコメントが見れるよ

mixiユーザー
ログインしてコメントしよう!

超初心者向けCGI講座 更新情報

超初心者向けCGI講座のメンバーはこんなコミュニティにも参加しています

星印の数は、共通して参加しているメンバーが多いほど増えます。

人気コミュニティランキング