- ベストアンサー
cgiでの並べ替えについて
フォームを使用して書き込みされたログファイルを並べ替えしているんですが、意図しない動きをするんです。 ログデータは以下のような感じです。 a=***&b=***&c=***&d=***&e=*** #-----並べ替えを行う open (FILE2,"<naisen.log"); flock(FILE2,2); @log2 = <FILE2>; flock(FILES,8); close FILES; @sort2 = sort {(split(/[&=]/,$a))[7] <=> (split(/[&=]/,$b))[7];} @log2; @sort3 = sort{(split(/[&=]/,$a))[5] cmp (split(/[&=]/,$b))[5];}@sort2; open (FILE2,">naisen.log"); flock (FILE,2); @filew = @sort3; print FILE2 @filew; close(FILE2); a=***&b=***&c=***&d=120 a=***&b=***&c=***&d=111 というデータがあった場合、意図している動きは a=***&b=***&c=***&d=111 a=***&b=***&c=***&d=120 という順番に並べ替えられるものですが、 a=***&b=***&c=***&d=120 a=***&b=***&c=***&d=111 です。 なぜ111の方が下にきてしまっているのでしょうか? また全部ではなく一部だけに並べ替えが適用されないのはナゼでしょうか?
- みんなの回答 (6)
- 専門家の回答
質問者が選んだベストアンサー
各補足など読みました。 実際に行いたい処理は、「項目cで文字列昇順にし、cの値が同じデータについては項目dで数値昇順にする」ということですよね? それならば、元のスクリプトや No.2 補足のスクリプトで目的の結果が得られるはずです。 ソースをコピーして実行したところ、No.3 お礼のw-intyさんの予想結果と同じになりました。 w-intyさんがテストしているソースには、質問に書き込んでいないコードとかが入っていませんか? また、質問などで *** と書かれているデータに、実際は & や = が含まれていた、などということはありませんか? 例えば、次のコードをコピーして実行してみてください。 ↓ ここから @log2 = <DATA>; @sort2 = sort {(split(/[&=]/,$a))[7] <=> (split(/[&=]/,$b))[7]} @log2; @sort3 = sort {(split(/[&=]/,$a))[5] cmp (split(/[&=]/,$b))[5]} @log2; print @sort3; __DATA__ a=***&b=***&c=3&d=126 a=***&b=***&c=3&d=125 a=***&b=***&c=2&d=111 a=***&b=***&c=2&d=128 a=***&b=***&c=2&d=120 a=***&b=***&c=2&d=112 ↑ ここまで No.3 お礼の2番目に書かれた予想結果と同じになりませんか? もしならなければ、お使いのPerlに問題がありますので、ソート部分を次のコードに置き換えてください。 @sort3 = map {$_->[0]} sort { $a->[6] cmp $b->[6] or $a->[8] <=> $b->[8] } map {[$_, split /[&=]/]} @log2; ※これで1行です。No.1 とは、sort の比較部分が異なります。 もし予想通りの結果になれば、__DATA__ より下のデータ部分に実際のデータをコピーしてテストしてみてください。 それでダメならデータに問題がありますので、データの内容や文字コード(全角文字が含まれるなら)などを差し支えない範囲で提示してください。 以下、余談です。 ・No.2 でタイプミスによりロックが外れていないとの指摘がありましたが、書き込み用 open のファイルハンドルが FILE2 となっているため、読み込み用に開いた FILE2 はいったん閉じられてロックも解放されるので、書き込みは問題なく行われます。 ・ソートの書式にも問題はありません。 組み込み関数などは、引数を囲むカッコを省略できます。 ・No.2 補足のスクリプトで、書き込み用 flock のファイルハンドルが FILE となっています。 また、読み込み時の flock の第2引数は、通常は 1 です。
その他の回答 (5)
- leaz024
- ベストアンサー率75% (398/526)
> やはりひとつひとつやっていったんではダメなんですかね? > (理論的にはあってると思ったんですが…) No.5 にも書きましたが、理論的にあってますよ。 ただしその理論は、sort 関数が「値が等しい要素の順番を保持すること」が前提ですよね? Perl5.8 の perldoc -f sort によると、「Perl5.6 およびそれ以前の Perl は、ソートのアルゴリズムが安定していない。安定したソートでは、値が等しい要素は入力順が保持される。」とあります。 つまり、もし w-inty さんがお使いの Perl が 5.8 でないなら、その前提がそもそも成り立っていない、ということです。 # 私も面倒がって、perldoc を読んでいませんでした^^; もし可能なら Perl5.8 をインストールしましょう。 それがダメなら、No.1, 5 で紹介した方法でソートしましょう。
お礼
回答ありがとうございました。 論理的にはあっていても、Perlが人間が思うように動いてないんですね。 人間のように応用はきかないですもんね(^^; 可能なら5.8をインストールしてみたいと思いますが、 当面は教えていただいた方法でいってみたいと思います。 ありがとうございました。
- yomo3
- ベストアンサー率32% (88/269)
そろそろ自信がなくなってきたぞ。 並べ替えのアルゴリズムにはいくつも方法があって、Perlの内部処理がどのようになっているか知らないのですが、基本は、1つずつキーを比較して、条件にあったら入れ替えるという処理を繰り返すのです。 問題は、比較の結果が等しいときに入れ替えを行うかどうかです。 もし入れ替えを行っているのなら、予想通りの結果にはならないと言うことになります。 これを回避する方法(sort関数を使って)はわかりません。 もしどうしてもということであれば、ループを使って手作りするということも考えられますが……。
- yomo3
- ベストアンサー率32% (88/269)
sortの書式についてはちょっと自信がありませんので、素直に引き下がります。 少なくとも、関数なのでsort();とカッコでくくるのが正解だとは思うのですが……。 このプログラムの意図するところがよくわからないのですが、1回目の並べ替えで、splitで分解された要素の7番目をキーに並べ替えていますよね。 このときには111が上に来ます。 2回目の並べ替えでは、同じく5番目の要素をキーにして並べ替えています。 ということは、1回目の結果に関係なく、d=***の***に従って並べ替えられると言うことです。 これは、途中で保存しても同じことです。
お礼
回答ありがとうございます。 ログの例をあげます。 1回目のソート(要素7)での並べ替えを行った結果… a=***&b=***&c=2&d=111 a=***&b=***&c=2&d=112 a=***&b=***&c=2&d=120 a=***&b=***&c=3&d=125 a=***&b=***&c=3&d=126 a=***&b=***&c=2&d=128 となりますよね?(ログファイルを上書き書き込み) で、その書き込みを行ったログファイルをさらに開いて、2回目のソート(要素5)を行うと… a=***&b=***&c=2&d=111 a=***&b=***&c=2&d=112 a=***&b=***&c=2&d=120 a=***&b=***&c=2&d=128 a=***&b=***&c=3&d=125 a=***&b=***&c=3&d=126 という結果になるかと私は思ったんですが、違いますか? ちなみに実際の結果は a=***&b=***&c=2&d=120 a=***&b=***&c=2&d=128 a=***&b=***&c=2&d=111 a=***&b=***&c=2&d=112 a=***&b=***&c=3&d=125 a=***&b=***&c=3&d=126 です。
補足
すいませんまたしても1箇所ミスが…。 2回目のソートのところの最後の配列は@sort2 ではなく@log2です。 修正しましたが、現象は変わらずです。
- yomo3
- ベストアンサー率32% (88/269)
タイプミスが大分あるようです。 例えば、ファイルハンドルはFILE2のはずなのに、途中でFILESになっています。 書き出しのとき、ファイルロックをはずしていません。 ソートの書式は、 @sort2 = sort({(split(/[&=]/,$a))[7] <=> (split(/[&=]/,$b))[7]} @log2); です。 それはともかく、 2回目のソートでひっくり返っているんじゃないですか?
お礼
回答ありがとうございます。 >タイプミスが大分あるようです。 あっ、ホントですね!(恥) @sort2 = sort {(split(/[&=]/,$a))[7] <=> (split(/[&=]/,$b))[7];} @log2; を @sort2 = sort({(split(/[&=]/,$a))[7] <=> (split(/[&=]/,$b))[7]} @log2); に変更したらエラーになってしまったんですが…。 >2回目のソートでひっくり返っているんじゃないですか? これは、たとえば1回目のソート前の状態に戻ってしまうということですか? 1度ログファイルの書きこみを行っても??
補足
#-----並べ替えを行う open (FILE2,"<naisen.log"); flock(FILE2,2); @log2 = <FILE2>; flock(FILE2,8); close FILE2; @sort2 = sort {(split(/[&=]/,$a))[7] <=> (split(/[&=]/,$b))[7];} @log2; open (FILE2,">naisen.log"); flock (FILE,2); @filew = @sort2; print FILE2 @filew; close(FILE2); print "@sort2<BR>\n"; open (FILE2,"<naisen.log"); flock(FILE2,2); @log2 = <FILE2>; flock(FILE2,8); close FILE2; @sort3 = sort{(split(/[&=]/,$a))[5] cmp (split(/[&=]/,$b))[5];} @sort2; open (FILE2,">naisen.log"); flock (FILE,2); @filew = @sort3; print FILE2 @filew; close(FILE2); と、1度並べ替えを行うごとに、ログファイルに書き込みを行うようにしました。 print "@sort2<BR>\n"; とかいてあるとおり、1度目のログファイルを表示させ確認したところ、この時の状態では、ちゃんと並べ替えは行われているようです。 ですが最後まで行われたログを見ると…。 1度書き込みを行ってもログファイルの並べ替えはうまくいかないものなのでしょうか?
- leaz024
- ベストアンサー率75% (398/526)
@sort3 は (split(/[&=]/,$_))[5] の値(つまり項目cの***)でソートした結果なのだから、項目dの値でソートされていないのは当然です。 ソートが2回行われていますが、この書き方だと1回目のソートはほとんど約に立っていません。 項目dで数値昇順にし、さらにdの値が同じデータについては項目cで文字列昇順にしたい、ということであれば、複合条件を使ってソートを行います。 @sort3 = sort { (split(/[&=]/,$a))[7] <=> (split(/[&=]/,$b))[7] or (split(/[&=]/,$a))[5] cmp (split(/[&=]/,$b))[5] } @log2; ただし、これだと比較の度に split が何度も行われるため非常に効率が悪いので、通常は次のようにします。 @sort3 = map {$_->[0]} sort { $a->[8] <=> $b->[8] or $a->[6] cmp $b->[6] } map {[$_, split /[&=]/]} @log2; このソートのアルゴリズムについては、参考URLをご覧ください。 # スクリプトに全角空白を使っているので、コピーする場合はタブなどに変換してください。
お礼
すばやい回答ありがとうございます。 複合条件ということで、1回目のソートが行われてないのかな?と思い、乏しい知識なので、 ログ読み込み→1回目のソート→ログ書き込み→ログ読み込み→2回目のソート→ログ書き込み としてみたのですが、それでも同じような結果になってしまったんですが…。
お礼
回答ありがとうございます。 教えていただいたsortの部分を @sort3 = map {$_->[0]} sort { $a->[6] cmp $b->[6] or $a->[8] <=> $b->[8] } map {[$_, split /[&=]/]} @log2; 使用してみたところ無事意図する動きになりました。 やはりひとつひとつやっていったんではダメなんですかね? (理論的にはあってると思ったんですが…)