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

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

超初心者向けCGI講座コミュのソート、あるいは正規表現

  • mixiチェック
  • このエントリーをはてなブックマークに追加
 めそめそコツコツ独学勉強しているうちにやっと、sortの項目に入りました。長い旅でした。
 アップした項目のINDEXを現在作っている最中なのですが、相当に野暮ったいプログラムになってしまいました。目下日にちの新しい順に並べている…というところなのですが、「あいうえお順」に並べるとか「カテゴリー別」に並べるとかいろんな並べ方を研究中です。

 何だか自分で書いていてわからなくなって来ているのですが、保存しているファイルは「0003-aiueo-ID756-DAT20051016.txt」こんな感じになっていて、「通し番号-ローマ字のフリガナ-IDナンバー-DAT日にち.txt」となっています。このファイルを読み込むと、「$number\t$year.$month.$mday\t($weekly[$wday])\t$hour:$min.$sec\t$title\t$spell\t$subtitle\t$category…」という順にデータが並んでいます。
 目次を作ろうと思うと、以下のようにファイル名を指定して一個ずつファイルを開いてタイトルを抜き出し…表示するという作業をしています。結構メモリを喰う野暮ったいやり方です。
 途中でソートしているのですが、これはファイル名のみでもってソートしているので、データの新しい順(古い順)で並べ替えることしかできていません。
 今、やろうと思っているのは、ファイル名のローマ字フリガナ(これは日本語タイトルのローマ字フリガナです。)を使って何とかアルファベット順に並べ替えてみようと思っていますが、どこから手を付けてよいのか?というところで進まなくなってしまいました。
 たぶん$filenameをsplitで切ってから、ファイル名のハイフン二つ目のローマ字のフリガナを通し番号と入れ替えて、それをソートしようかと思ったんですが、そうしてしまうとファイル名が違うものになってしまうんです。
 こういう時って通常どんな風に並べ替えをするんでしょうか?
 
 


opendir(DIR, "./datafile");
while (my $filename = readdir(DIR)) {
next if $filename eq '.';
next if $filename eq '..';
unshift(@indexlist,$filename);
}
closedir(DIR);
my @indexlist = sort @indexlist;
foreach (@indexlist) {
open TXT, '<' . './datafile/' . $_ or die $!;
my $line = <TXT>;
@_ = split /\t/ , $line; local ($no,$date,$wday,$time,$title,$id,$nannda,$category,$nanda2,$text,$count_letter,$imgpath,$aa,$bb,$epok) = @_;
print "<tr>\n";
print "<td style=\"border-top:solid 1px;border-color:#000000;\" valign=\"top\">$date:</td>\n";
print "<td style=\"border-top:solid 1px;border-color:#000000;\">\n";

if ($epok > $times-(86400*3)) { #NEWの表示
print "<img src=\"../img/tool/new.gif\"><a href=\"./textout.cgi?$_\" class=\"title\" target=\"_parent\">$title</a>";
} elsif ($epok < $times-(86400*3)) {
print "<img src=\"../img/tool/enpitsu.gif\"><a href=\"./textout.cgi?$_\" class=\"title\" target=\"_parent\">$title</a>";
}
print "</td>\n";
print "</tr>\n";
close TXT;
}

コメント(15)

私の場合だと

while (my $filename = readdir(DIR)) {
next if $filename eq '.';
next if $filename eq '..';
unshift(@indexlist,$filename);
}

の部分を

foreach(sort{(split(/\-/,$a))[1] cmp (split(/\-/,$b))[1]} readdir(DIR)) {
next if $filename eq '.';
next if $filename eq '..';
unshift(@indexlist,$filename);
}

のように書くかもしれません。
みずのさん上手いですなぁ。自分は全然浮かばなかったです。

自分だったらファイル名⇔内容の対応表ファイルみたいなのを作っておくかなぁ、と思います。
みずのさんいつも世話になります。
foreach(sort{(split(/\-/,$a))[1] cmp (split(/\-/,$b))[1]} readdir(DIR)) {
…なるほど、はじめて見ました。僕もそろそろ中級者以上の本を買おうと思っていますが、こんな書き方ってできるんですね。びっくりです。
 これって、ローマ字フリガナのところを読み込みながら直接ソートいるんですよね?
 
 直接その部分だけ変えてみたら、こんなエラーのようなものがでました。
No such file or directory at C:\WWW\public_html\tangsten_psycology\xxregist\alli ndexout.cgi line 48.
 このやり方だと、$filename自体はそのままでソートする時のみ部分的な場所を比較しているってことなんでしょうか?
 だとしたら、ディレクトリとかファイルとかは、変わっていないので動く筈なんですが…。。(悪戦苦闘!)
あ。

foreach $filename (sort{(split(/\-/,$a))[1] cmp (split(/\-/,$b))[1]} readdir(DIR)) {
next if $filename eq '.';
next if $filename eq '..';
unshift(@indexlist,$filename);
}

もしくは

foreach (sort{(split(/\-/,$a))[1] cmp (split(/\-/,$b))[1]} readdir(DIR)) {
next if $filename eq '.';
next if $filename eq '..';
unshift(@indexlist,$_);
}

ですね。
ズレますが

> my @indexlist = sort @indexlist;

この記述はなんかあやしげ。
use strict使ったほうがいいすよ

あと気になるのは
・opendir(DIR, "./datafile");
・open TXT, '<' . './datafile/' . $_ or die $!;

関数の場合、かっこが省略できますが、どちらかのスタイルに統一する(かっこをつけるのを僕は推奨。わかりやすいので)方が最初はいいと思います。

だって
・@_ = split /\t/ , $line;
こうなると一瞬意味がわからんでしょう?
  みずのさん、kentさん、KIDD@TV募集中さん、いつも世話になってしまいます。

>KIDD@TV募集中さん
> my @indexlist = sort @indexlist;
>この記述はなんかあやしげ。
え!これってあやしげなんですか?
 use strictを久しぶりに使ってみたら、相当にマズイことになってました。ありとあらゆる箇所がおかしなことになってます(汗)。。。もう一度見直さなきゃいかんという感じです。
・open TXT, '<' . './datafile/' . $_ or die $!;
↑これ、ちょっと調子にのってやってみました。僕も相当に見づらいです。しかし、なんでこんな書き方で同じ動き方するんでしょうね。
my @indexlist = sort @indexlist;
も
@indexlist_a = sort (@indexlist);
とかいう風に違う変数に入れ替えたほうがいいんでしょうか?

ちょっとまたいじくってみて、また来ます!!
foreach $filename (sort{(split(/\-/,$a))[1] cmp (split(/\-/,$b))[1]} readdir(DIR)) {
next if $filename eq '.';
next if $filename eq '..';
unshift(@indexlist,$filename);
}

 これでばっちり動きました。
 しかし、こんな書き方ができるんですね。
foreach $filename(…←とやると動くんですが、
foreach… ←だと駄目でした。読み込む変数を指定していないと駄目ってことですよね?

 このスクリプトでちょっと思ったんですが、いろいろなカッコがありますでしょ?[]、()、{}…と。。カッコの使い方に関して僕は覚えた通り暗記しているだけなんですが、何か普遍的な法則みたいなものがあるんですか?
 例えば{xxx(oooo[zzzz])}の順に使えるとか…。なんかそんな気がしてきたのです。
$in{key}という時は絶対に{}しか使えないし、$_[0]でも[]しか使えない。でもカッコの省略とかができるところを見ると、何だかちょっとした法則の元にカッコが使われているような気がしたのです。

 みずのさん、ありがとうございます。

 と、トピックが終わろうというところなのですが、もうちょっとつき合ってください。
 sortする時には、<=>が数字の比較演算子、cmpは文字の比較演算子ということなんですよね?で、cmpは文字コードを比較してsortするっていうことなんでしょうか?
あれ?
ちょっと考えたのですが、

foreach $_(sort{(split(/\-/,$a))[1] cmp (split(/\-/,$b))[1]} readdir(DIR)) {
next if $_ eq '.';
next if $_ eq '..';
unshift(@indexlist,$_);
}
ですよね。変数を指定しなかったら、$_だった筈!
なんか、わかるようでわからない…悶々中の僕であります。
自己レスから

>(かっこをつけるのを僕は推奨。わかりやすいので)

PerlBestPracticesという本によると

組み込み関数にはかっこはつけるべきじゃないそうです。(笑)

さて
> my @indexlist = sort @indexlist;

これが怪しげと書いた理由は左辺で配列の変数の宣言をしているにも関わらず、すでにその変数を右辺で使っているからです。

もっというと、
> unshift(@indexlist,$filename);

より前で@indexlistはmy(なりなんなりで)宣言すべきなのです。それをしていないから、use strictでどえらいことになると思います。


まぁこの話書こうと思ったのは
http://d.hatena.ne.jp/naoya/20051028/1130495012
このへんが発祥とかいう。
あぁ。すいません。さらに間違えてましたね。。

foreach (sort{(split(/\-/,$a))[1] cmp (split(/\-/,$b))[1]} readdir(DIR)) {
next if $_ eq '.';
next if $_ eq '..';
unshift(@indexlist,$_);
}

こうですね。。

cmpについては、文字コード順になります。
 みずのさん、ばっちり動きました。でも自分では全然思いつけないフレーズです。
 cmpは文字コード順なんですね。<=>が数値順…、それで今ふと思ってカタカナの文字コード(僕はshift_jisを使っているので、shift_jisで)を調べてみると、「ア」がB1…で、どんどん続いて「ン」がDDなんです。これって順番にならんでいるんですが、ソートした時にはアイウエオの辞書順になるってことなんですよね?問題は、濁点と○点がンよりも最後の文字コードになっているんです。アイウエオ順(辞書順)にソートしたいときはそれなりの工夫をしなくてはならないということですよね?(僕はとりあえず濁点は無視!みたいな感じで辞書順に近く並べ替えるというところから始めないと理解できないような気がします。。。)

KIDD@TV募集中 さん、
 むむむ、宣言という問題ですね!Perlは変数の宣言があんまり厳しくないから、結構夢中になって書いて動かしてみて動いてしまったら最終的に膨大なプログラムになっていて、自分で設定した変数が何を示しているのかわからない始末です。
 宣言は面倒だけど、複雑になればなるほど大切かも…ですねぇ。
>問題は、濁点と○点がンよりも最後の文字コードになっているんです。

半角カタカナで・・・ってことでしょうか?
ちゃんとやりたい場合はフリガナを使いますよね。
ただ、その場合は全角の平仮名や片仮名を使用するかと思います。

82A0-82FFくらいの範囲にありますので確認してみてください。
表を見る限りでは問題は発生しないと思います。
最近気づいたこと。「ヴ」は問答無用で「ン」より後ろに来ました。読み仮名でソートしてて気づいたんですけどもね。
あぁ。カナだとそうなりますねー。
ヴは難しいですねw

難しく考えると、ソート用のデータベースを用意しておく感じになるでしょうねー。

50音くらいなら、そんなに難しく無いでしょうし、良いかもしれません♪

俺は普通に ウィンテージ とか、ウァンヘルシングとかって書いちゃうかもです 笑
 ヴですか…。考えてませんした(笑)。ヴェートーベンはワーグナーよりも後に表記されてしまうってことですね。

>82A0-82FFくらいの範囲にありますので確認してみてください。 表を見る限りでは問題は発生しないと思います。

 本当ですか?(って、自分でやって見れ!って話なんですが…)表を見ると「ン」よりも後にあったんです。が、もっとよく調べてみたら大丈夫そうですね。
 半角カタカナはいろいろと大変そうなので、いつも後回しにしてます(笑)。。。全角でやってみます。
 ちょっと面倒だけど、フリガナによるソートのサブルーチンを一度作ってしまえばいいんですよね。がんばってみます。
 

ログインすると、みんなのコメントがもっと見れるよ

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

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

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

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

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