PHPで次の物を作成してください。

私の環境で動く物を最初に完成された方に300ポイント、他二人まで50ポイント差し上げます。

下記のようにデータが一行記入された復数のCSVファイルが特定のフォルダ内にあります。
1116.csv
愛知県,山田太郎,Yamada Taro,無,教師

4564.csv
愛知県,田中一郎,Tanaka Ichirou,有,プログラマー

hoge.php 内のフォームで検索して、マッチしたキーワードを含むCSVファイルがあった場合、名前、職業、ファイル名を表示するというプログラムを作成してください。つまり「愛知県」が検索された場合次のように表示されます。
フォーム
田中一郎 プログラマー 4564.csv
山田太郎 教師 1116.csv

ただし下記の点にご留意ください。
・コメントは多めに振ってください。
・CSVファイルがあるフォルダのパスはお任せします。
・表示結果はCSV内に「有」と書れている物が上に、次にローマ字名の順でソート。
・hoge.phpの文字コードはUTF-8なので、文字化け対策もお願いします。
・復数のキーワード検索は半角か全角のスペースで区切ったand検索。
・セキュリティもご配慮ください。

環境はXP、Xampp、PHP5です。
よろしくお願いします。

回答の条件
  • 1人2回まで
  • 登録:2010/07/19 13:40:55
  • 終了:2010/07/21 20:28:39

ベストアンサー

id:windofjuly No.1

うぃんど回答回数2625ベストアンサー獲得回数11492010/07/19 20:08:43

ポイント300pt

毎回毎回ファイル検索させるのは非常に効率が悪い事だと知っておられると思いますし、そのため、このような処理は通常行うことがなく、今回用にゼロから作ってみました

作った際のチェックポイントも一部コメントの形で残してありますので活用してもらえればうれしいですね

Windows版のxamppは使っておらず Linux版で独自にビルドしたものを組み合わせて使ってますので、万全とは言えませんし、

phpでは同じことを実現できる関数が幾重にも存在する場合があり、バージョンによって細かに違う場合もありますので、下記が万能というわけではありません

詳細はコード中のコメントを読んでください

<?php
    // 実際に設置する際にはapacheの仮想ディレクトリ外にcsvファイルを置かなければならないが、ここではスクリプトと同一ディレクトリとする
    $dir = '.';
    //
    // パラメータkeywordsが存在しなければスルー
    if ( isset( $_POST["keywords"] ) ) {
        $keywords = preg_split( '/(\s| )+/', strip_tags( $_POST["keywords"] ) ); // タグを取り除いた後、キーワードを分割
        // パラメータkeywordsが空ならばスルー
        // echoで文句を言ってもいいけどね(笑)面倒だから無視してスルー
        if ( !empty( $keywords[0] ) ) {
            $pt = '/(?=.*' . join( ')(?=.*' , $keywords ) . ')/i'; // AND検索用パターン作成
            // 与えられたキーワードと作成したパターンの表示
            print_r($keywords);
            echo '<br />' . $pt . '<hr />';
            /*
                // AND検索のテスト 仮のデータ$aと比較して適合すれば OK を表示
                echo $a = ' あかさたな はまやらわ かきくけこ さしすせそ';
                if ( preg_match( $pt, $a )  ) echo ' OK';
                echo "\n";
                echo $a = ' あかさたな はまやらわ かきくけこ えあいうえおさしすせそ';
                if ( preg_match( $pt, $a )  ) echo ' OK';
            */
            // 該当した情報を格納する配列を準備(後から有無を混在させたソートをするのは面倒なので有無で最初から分けることにした)
            $list1 = array(); 
            $list2 = array();
            $errorList = array(); // 内容に不備があるcsvのリスト、チェック不要なら要らない
            $dh = opendir( $dir ) or die( "error" ); // dirを開く
            while ( ( $file = readdir( $dh ) ) !== false ) {
                // ファイル名で絞込み
                if ( preg_match( '/\d{4}.csv$/', $file ) ) {
                    // 便利なfgetcsv関数も存在するけれど文字化け対策としてfile_get_contentsで読み込んでから分割する手順とした
                    // Windows上のcsvファイルなので文字コードを判断する優先順位の1番目にsjis-winを明示
                    $readFile = mb_convert_encoding( file_get_contents( $file ), "UTF-8", "sjis-win, SJIS, UTF-8" );
                    if ( preg_match( $pt, $readFile )  ) {
                        /*
                            // 対象ファイルの中身を文字化けしたりせずに正しく読み込んでいるかどうかのテスト出力
                            echo $readFile . "<br />";
                        */
                        $csv = split( ',', $readFile ); // 1行目をカンマで分割
                        // csvの中身を項目数で簡易的にチェック。チェック不要ならキーとしてリストに追加だけすればいい
                        if ( count( $csv ) == 5 ) {
                            // 同姓同名の存在も考えて、ローマ字表記+ファイル名をキーとしてリストに追加
                            if ( $csv[3] == '' ) {
                                $list1[($csv[2] . $file)] = join( ',', array( $csv[1], $csv[4], $file ) );
                            } else {
                                $list2[($csv[2] . $file)] = join( ',', array( $csv[1], $csv[4], $file ) );
                            }
                        } else {
                            $errorList[] = $file;
                        }
                    }
                }
            }
            closedir($dh);
            // キーでソート
            ksort( $list1 );
            ksort( $list2 );
            // キーでソートしたものを簡易出力
            // 変数に格納された値の出力方法や、テーブルでの出力方法は、前回ご質問のものを参照して改良してください
            // http://q.hatena.ne.jp/1278940933
            // トラブル時に原因をつかみにくくなるので、何度もテストして、処理の内容もよく把握できるようになるまでは簡易出力のままがよいです
            echo '<hr /><pre>';
            print_r( $list1 );
            print_r( $list2 );
            echo '</pre><hr />';
        }
    }
?>
    <form action="<?= $_SERVER['SCRIPT_NAME'] ?>" method="POST">
        <input type="text" name="keywords" size="50"><br>
        <input type="submit" value="送信" />
    </form>
id:taroemon

私の環境で動きました。

丁寧なコメントありがとうございました。分かりやすかったです。

$list1の配列の使い方は僕にとって斬新で参考になりました。

str_getcsvはまだ新しい関数なので、explodeを使うことにします。

ありがとうございました。

2010/07/21 20:27:59
  • id:Mook
    回答は差し控えますが、
    DB 導入したほうがよい案件ではないですか。
  • id:GoldenDawn
    データのあるフォルダの中身はデータだけなのか、あるいは拡張子が csv のものだけを処理すればいいか。
    フォームが hoge.php にあるとして、出力するのも hoge.php なのか。
    リクエストは GET と POST のどちらが望ましいか。
    キーワードは完全一致が部分一致か。
  • id:yamaneroom
    yamaneroom 2010/07/19 15:28:20
    要件が曖昧すぎる。

    >hoge.phpの文字コードはUTF-8なので、文字化け対策もお願いします
    CSVファイルはUTF-8ではないということか?

    >セキュリティもご配慮ください。
    何を配慮すればいいのか?
    ローカルで利用するプログラムではないのか?
    公開プログラムだとすると、ユーザーの範囲は?
    個人情報保護はどうするのか?
  • id:taroemon
    みなさんコメントありがとうございます。
    質問できる文字数が限度いっぱいになってしまったので、説明不足になってしまったことをお詫びします。
    頂いたご回答は参考にするもので、そのままコピペして使うことはありません。
    実際に使うにあたって過不足があれば、自分で解決するなり、改めて質問しなおすなりします。
    なのでフォームのGETとPOSTなど指定がないところは回答者の方の判断にお任せします。

    >DB 導入したほうがよい案件ではないですか。
    私もそう思うのですが、CSVが条件になっています。

    >データのあるフォルダの中身はデータだけなのか、あるいは拡張子が csv のものだけを処理すればいいか。
    フォルダの中には他の拡張子のファイルもあるので、
    拡張子がcsvファイルの中にあるデータだけを抽出するというものが望ましいです。

    >リクエストは GET と POST のどちらが望ましいか。
    個人的にはPOSTが良いです。

    >CSVファイルはUTF-8ではないということか?
    これは指定がありませんでした。友人が使うものなので、いくつか指定がないことがります。

    >(せキュリティは)何を配慮すればいいのか?
    基本的にはローカルで使用するものすが、セキュリティに自信が持てれば限られた範囲内で公開されるものになります。
    今回はどういう手法があるか参考になればそれで良いです。
  • id:taroemon
    今回はPHPの勉強をすることを目的としています。
    質問には個人情報に関するデータが書かれてますが、実存する個人のデータを使用することはありません。
    これを叩き台にして、いつかそういうシステムを作る時が来るかもしれませんが、
    今回は友人との勉強を目的としていますので、多少セキュリティに穴があっても問題ありません。
    「こういうところを気をつけたほうがいいよ」みたいな気持ちでご回答いただければ幸いです。
    なので、他の部分で説明が不足しているところは回答者の方が判断していただいて結構です。
    それを自分たちに都合よくカスタマイズすることも勉強の一環と考えてます。
  • id:taroemon
    あとキーワード検索は部分一致が望ましいです。
  • id:Mook
    >今回はPHPの勉強をすることを目的としています。
    ということであれば、個人的には余計 DB を使用すればと思うのですが、
    DB に対して抵抗があるのでしょうか。

    CSV を使用するとして、情報をファイル単位で持つのはなぜでしょうか。
    せめて 1ファイルで管理すればよいと思うのですが。

    勉強ということであれば、なおさら、あまり自分の発想に固執せず、
    一般的な使用方法を身につけたほうが良いように思います。
  • id:tezcello
    丸投げの質問には答えない主義なので、コメントだけ。

    > キーワード検索は部分一致
    ホントにどこでもいいのかなぁ?
    名前が、川愛知子(Kawai Tomoko)さんとか、愛知川太郎(Echigawa Tarou)さんとか
    職業が、愛知県職員とか、JA愛知○とか


    個人的には、DBを使わずにあれこれ考えるのは、データの持ち方とか正規表現とかアルゴリズムとかを思考する練習になると思います。
    ただ、その部分を質問として投げてしまっている点で、質問者さんには殆ど勉強にならないでしょうけど。(先に「答え」を見てしまうと、考える力は身に付かないと思うので)
  • id:windofjuly
    うぃんど 2010/07/20 01:55:57
    taroemon さんへ

    回答1の冒頭でも触れましたように私も実利用ではDBを使うことを推奨しますが、今回はファイル操作の基本的なものというつもりで回答させてもらいました
    データはcsvファイルからMySQLへ直で読み込ませることもできますけど、phpで内容を精査してからDBへ登録という流れもしばしば行うことなので、ファイル操作も重要なものだと考えるからです

    ファイルが1番から4564番まであるとすれば、それ相当の処理時間になるはずですから、ご友人のほうも、あまりの遅さに絶句するかもしれませんが、それはどうしようもないことなので、あきらめてもらうか、DBの利用を考えてもらうきっかけになるでしょう(友人であれば、そのあたりは事前にしっかり理由を説明してあげて、最初からDB利用にするべきだと思いますが、どの程度親しい友人かもわからないので、このあたりは深く詮索しないでおきますね)

    ここからが本題ですけど、時間をおいて見直すと、自分でもいろいろ気になるところがでてきましたので追記します
    下記は急いで修正を施さなくてはいけないという種類のものではないので回答1の動作確認後に修正するようにしてください
    【回答1の修正1】ファイル名で絞込みのパターンは ^ を付けた方がいい
    '/^\d{4}.csv$/'
    【回答1の修正2】下記のようにすれば $list1 と $list2 に分けなくてよくなり $list1 だけでよくなります
    if ( $csv[3] == '有' ) {
    $list1[( '1' . $csv[2] . $file )] = join( ',', array( $csv[1], $csv[4], $file ) );
    } else {
    $list1[( '2' . $csv[2] . $file )] = join( ',', array( $csv[1], $csv[4], $file ) );
    }
    【回答1の修正3】split を使ってしまいましたが explode のほうが良いです。php5.3.0以降にはstr_getcsv関数もありますので特徴を学んでおくといいでしょう
    http://jp.php.net/manual/ja/function.split.php
    http://jp.php.net/manual/ja/function.explode.php
    http://jp.php.net/manual/ja/function.str-getcsv.php
    【回答1の修正4】エラー出力してなかった
    print_r( $errorList );
    【回答1の修正5】エラーファイルの中身を出力したい場合はファイル名をキーとして格納するといい
    $errorList[$file] = $readFile;
  • id:taroemon
    皆さんコメントありがとうございます。

    >Mookさん
    DBを使ったほうが良いというご意見には全く賛成です。
    苦手意識のようなものはまったくありません。
    これは友人からのリクエストなのでどうしようもないところがあります。
    相手が「今回CSVでやりたい」と主張したら、「そうですか」というしかない状況です。
  • id:taroemon
    >tezcelloさん

    >丸投げの質問には答えない主義なので、コメントだけ。
    いつもお世話になってます。ありがとうます。

    >名前が、川愛知子(Kawai Tomoko)さんとか、愛知川太郎(Echigawa Tarou)さんとか
    >職業が、愛知県職員とか、JA愛知○とか
    まったくおしゃる通りです。しかしそれらのことを一つ一つはてなの質問で説明するのは難しいです。
    ある程度意に沿わないことが出ることは想定済みです。それは自分たちで解決するつもりです。

    >その部分を質問として投げてしまっている点で、質問者さんには殆ど勉強にならないでしょうけど
    ご意見は承りました。反論はしないでおきます。
  • id:taroemon
    >windofjuly

    >phpで内容を精査してからDBへ登録という流れもしばしば行うことなので、ファイル操作も重要なものだと考えるからです
    ご理解ありがとうございます。

    >それはどうしようもないことなので、あきらめてもらうか、
    了解しました。遅いのであるなら良いきっかけになると思います。

    >ここからが本題ですけど、時間をおいて見直すと、自分でもいろいろ気になるところがでてきましたので追記します
    丁寧にありがとうございます。まだ試してない段階なので問題ありません。
    1~2日内に試してお返事します。
  • id:taroemon
    コメント欄でDBについていろいろご意見を頂いてるので、
    つけ加えますが、私はつたないながらもDBを使うことができます。
    今回のことも本音ではDBを誓うべきだと考えてます。
    その上で、あえてCSVファイルで処理することを選択したということです。

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

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

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

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