- 締切済み
更新記録CGIで「前へ」「次へ」の記事を出す方法
更新記録CGI(perl)を利用して、それを改造したいと思っています。 記事は1つだけ表示して、記事下に「←次へ」「前へ→」のようなリンクを作って前後に移動したいです。 素人考えで以下のようにしました。 $no =5; (現在の記事NO。数字が入ります) $new_no = $no--; $old_no = $no++; print "<a href=\"news.cgi?vno=$new_no\">「←次へ」</a>\n"; print "<a href=\"news.cgi?vno=$old_no\">「前へ→」</a>\n"; しかしこれだと、2つの問題が発生します。 1.最新記事・最古の記事の次または前ページが存在しないのに行ってしまう。 2.何らかの理由で記事を消した場合でもそこに行ってしまう。 この2点を回避するにはどうしたら良いでしょうか? $no =5; open(IN,"$logfile") || &error("Open Error : $logfile"); while (<IN>) { ($no,$year1,$month1,$day1,$name,$tail1,$W1,$H1,$item1,$item2,$item3,$item4,$item5,$host) = split(/<>/); <<<--- どうにかして、この記事の次記事があるかを判別する --->>> if($new_no){ print "<a href=\"news.cgi?vno=$new_no\">「←次へ」</a>\n"; }else{ print "<font color=#aaaaaa>「←次へ」</font>\n"; } <<<--- どうにかして、この記事の前記事があるかを判別する --->>> if($old_no){ print "<a href=\"news.cgi?vno=$old_no\">「前へ→」</a>\n"; }else{ print "<font color=#aaaaaa>「前へ→」</font>\n"; } このようにしたいのですが、方法を教えてください。
- みんなの回答 (4)
- 専門家の回答
みんなの回答
- kmee
- ベストアンサー率55% (1857/3366)
> これをしたいのですが、どのように記述したら良いかがわからないほどの初心者です それなら、まずは、このやり方に見当がつくくらいの勉強をしてからの方がよいのでは? ・テキストファイルを全部リストに入れる方法 ・リストの並び換えの方法(sort関数のちょっと高度な使い方) などは、初級の入門書にも載っていると思います。 ※ 他にも 「3行だけ記録して、真ん中が該当行だったら前後の記事番号を使う」 「注目記事番号未満のうちの最大値と、注目記事番号超のうちの最小値を求める」 等の手法もあります。 それから、++ / -- はちゃんと理解してください。 単に「1減らした数」ということにはなりません。 変数の前に書いたときと後に書いたときとの違いも重要です。 理解できていないのなら、使わない方がいいくらいです。 $new_no=$no++;#新しい記事だから現記事より+1 とありますが、 後に置いた場合の ++ は ・まず、元の値を使用する ・使った後でその変数自体を+1する という効果があります。つまり、この文は { $new_no=$no; $no=$no + 1 ; } とほぼ同じことになります。同様に $old_no=$no--;#古い記事だから現記事より-1 は { $old_no=$no ; $no = $no - 1 ; } です。 では、最初に$no=5として $new_no=$no; #=> $new_no = ? $no=$no + 1 ; #=> $no = ? $old_no=$no ; #=> $old_no = ? $no = $no - 1 ; #=> $no =? としたら、それぞれどうなりますか? 上の?をそれぞれ埋めてみてください。
- kmee
- ベストアンサー率55% (1857/3366)
> ログファイル開く→現ページを確認→それに+1と-1した数字を作る→whileでヒットしたところを表示 考えていけば、いくらでも方法はありますが、とりあえずは、その方針でいいのでは? あとは、ちょっとしたアイディアだけです。 例えば: ○「数字を作る」のではなくて、-1番目、+1番目を探す。 全行を記事番号順にソートして、 @l に保存 $l[$i] が$noの記事なら、 $l[$i-1] と $l[$i+1]が目的の「前後の記事」。 $i==0なら「前の記事」は無いし、$i+1と@lの長さを比較すれば「後の記事」が存在するかどうか判定できる 他にも: 前の記事番号 = $noより小さい記事番号の最大値 後の記事番号 = $noより大きな記事番号の最小値 であることに注目する それ以前の問題として、no++/no--では期待する番号になっていない、というのは、既に指摘のある通りです。
- Tacosan
- ベストアンサー率23% (3656/15482)
「プログラムがあるからデータ構造もわかるだろ」ってのは, 質問としては乱暴だなぁ. 現実的な問題として どうもきちんとデータか読めないと思ったら, 実はプログラムが間違ってました ってこともあるわけなので, データ構造はデータ構造として書いておくべきだと思うよ. さておき, 大事なことを聞き忘れていました. ・「削除」すると, データとしてはどうなるんですか? 当該行が消えるのか, それともどこかに「削除したよ」って情報が記録されるのか, あるいはそのどちらでもない別の方法で管理する? ・(こっちはさほど重要じゃないけど) 順番はどうなっていますか? いずれにしても方向性としては「適切なデータ構造を作る」ことになるわけだけど, その「適切なデータ構造」が条件によって変化する可能性はある. あと余談だけど, $vno が 5 のとき $new_no = $vno--; $old_no = $vno++; によって $vno, $new_no, $old_no の値がどうなるか答えられますか?
補足
返答ありがとうございます。 >データ構造はデータ構造として書いておくべきだと思うよ. すみません。そういう事以前に、何をどう記入したら良いかさえ分かっていない初心者なので、あのような書き込みになっていまいました。 細かく指示をもらえるととてもありがたいです。 >「削除」すると, データとしてはどうなるんですか? その行が消えます。 ログに、 5<>2014<>1<>1<>新着題名5<>~ 4<>2014<>1<>1<>新着題名4<>~ 3<>2014<>1<>1<>新着題名3<>~ 2<>2014<>1<>1<>新着題名2<>~ 1<>2014<>1<>1<>新着題名1<>~ とあって、4番目の記事を削除すると 5<>2014<>1<>1<>新着題名5<>~ 3<>2014<>1<>1<>新着題名3<>~ 2<>2014<>1<>1<>新着題名2<>~ 1<>2014<>1<>1<>新着題名1<>~ となります。 こういうログで、該当する記事を表示させたい場合、「~news.cgi?vno=5」のようにして、内部では、 while (<IN>) { ($no,$year1,$month1,$day1,$name,$tail1,$W1,$H1,$item1,$item2,$item3,$item4,$item5,$host) = split(/<>/); if ($in{'vno'} == $no) { print"$name<br>~\n"; } } で記事を探します。 この方式だと、$noに1をプラスマイナスした場合、消された記事に行ってしまうことになりますよね… >あと余談だけど, $vno が 5 のとき すみません、これも書き間違えですよね while (<IN>) { ($no,$year1,$month1,$day1,$name,$tail1,$W1,$H1,$item1,$item2,$item3,$item4,$item5,$host) = split(/<>/); if ($in{'vno'} == $no) { print"~\n"; $new_no=$no++;#新しい記事だから現記事より+1 $old_no=$no--;#古い記事だから現記事より-1 #↑この位置なら$in{'vno'}は使う必要なしですよね? } } で、 $vnoを5としたら $new_noは6、$old_noは4と思ってますが、間違ってないでしょうか? 今これを記入しながら少し考えたのですが、該当する記事番号が無ければさらに+1(-1)すると言うのはどうでしょう? 次・前のリンクをそれぞれ ~news.cgi?vno=$new_no&move=new ~news.cgi?vno=$old_no&move=old とし、 while (<IN>) { ($no,$year1,$month1,$day1,$name,$tail1,$W1,$H1,$item1,$item2,$item3,$item4,$item5,$host) = split(/<>/); if ($in{'vno'} == $no) { print"~\n"; $new_no=$no++;#新しい記事だから現記事より+1 $old_no=$no--;#古い記事だから現記事より-1 #↑この位置なら$in{'vno'}は使う必要なしですよね? #この位置に前後へのリンク }else{#該当記事が無ければ if($in{'move'} eq $new){ $vno++; &view; #この表示をするサブルーチンに戻る exit; } if($in{'move'} eq $old){ $vno--; &view; #この表示をするサブルーチンに戻る exit; } } … すごく回りくどいことをしているようにも思いますが… でも、これだと、最初の記事・最後の記事にたどり着いた際、おかしな挙動になりますよね… すみません、私の知識ではこの程度しか考えられません。 なんとなくレベルがわかっていただけるとうれしいです。
- Tacosan
- ベストアンサー率23% (3656/15482)
まずもって ・各記事をどう保存しているのか を書いてくれないことには話にならない. あと, 「何らかの理由で記事を消した場合」にどうするのか (記事NO が 4 の記事を消した場合, 記事NO 3 の記事の「前」と「次」はそれぞれ何になるのか) も明確にしておく必要がある. ついでに: $no =5; (現在の記事NO。数字が入ります) $new_no = $no--; $old_no = $no++; で $no, $new_no と $old_no の値がそれぞれ何になるかわかりますか? $no =5; open(IN,"$logfile") || &error("Open Error : $logfile"); while (<IN>) { ($no,$year1,$month1,$day1,$name,$tail1,$W1,$H1,$item1,$item2,$item3,$item4,$item5,$host) = split(/<>/); # 以下略 } とやると最初の $no =5; の意味がなくなってしまうことは理解できていますよね?
補足
すみません色々抜けてました。 >各記事をどう保存しているのか datファイルに$no,$year1,$month1,$day1,$name,~ の順で、"<>"で区切って保存しています。 (open~while~の部分で理解していただけると思ったので記入したのですが、書き方が間違っていたでしょうか…) >記事NO が 4 の記事を消した場合, 記事NO 3 の記事の「前」と「次」はそれぞれ何になるのか 4を消した場合、3の記事の場合は5と2の記事に行くようにしたいです。 >no =5;の意味がなくなってしまう すみません書き間違えです。 ページを表示するのはvnoですね。 ~news.cgi?vno=5 で5記事目を表示します。 またそれぞれ $new_no = $vno--; $old_no = $vno++; です。 私のスキルでは、 ログファイル開く→現ページを確認→それに+1と-1した数字を作る→whileでヒットしたところを表示、としかできません。 それをどうにかして「ログの前後の記事を表示させる」としたいです。 引き続きよろしくお願いします。
補足
返答ありがとうございます。 >全行を記事番号順にソートして、 @l に保存 >$l[$i] が$noの記事なら、 $l[$i-1] と $l[$i+1]が目的の「前後の記事」。 これです!これをしたいのですが、どのように記述したら良いかがわからないほどの初心者です。 出来れば具体的にどうしたら良いかを教えていただけるととてもうれしいです!