ファイル操作

C言語プログラムとほぼ同様の操作である。 今回は,2つの実習を行う.

  1. カンマで区切られたデータ(csv形式ファイルと呼ぶ)を読込んで,表の形式でhtml出力すること.
  2. あるディレクトリに納められたデータファイルを探しだし,ファイル名付きで表示すること.

下準備

授業開始までに次のことを行っておく.

  1. プログラムを作成するディレクトリ名を決め,作成すること.(前の練習で行ったものそのままでよい.)
       mkdir ~/public_html/softwareDevel
    
    など.
  2. 河川水質調査の仮想データを実習に用いる(演習2から).3つのデータファイルを連結圧縮したものがdata/data_softwareDevel.zipである.これをダウンロードして,データ格納用ディレクトリにおく(学内だけのアクセスの制限してある).

プログラムをおくディレクトリの下にdata用のディレクトリを用意し、そこの解凍したファイルを置く。

教室のWindowsを使っている場合は,Uドライブの"U:\public_html"に,たとえば"softwareDevel\data"というフォルダを作って,そこにzipファイルを展開する.

ファイルを読み込んで処理をおこなう

ファイルを扱う基本は

    $handle =  fopen("ファイル名", "モード");
  

    $buffer = fgets($handle);
  

    fclose($handle)
  

参照ページ:「初心者用PHP入門」のファイルの読み書きの章

(ファイルを扱う関数の詳細は,マニュアルのファイルシステムを参照する.)

例: ファイルを開き,1行ごと読込み,そのまま画面に出力する

   <?php
   $handle = @fopen("data/sample1.csv", "r");
   if ($handle) {
      while (!feof($handle)) {
        $buffer = fgets($handle);
        echo $buffer;
      }
      fclose($handle);
   }
   ?>

ここで"@"は「エラー制御演算子」で,右側の式(この場合はfopenの実行)でエラーが生じた場合,エラーメッセージが左辺に代入されることなく無視するものである.(別のグローバル変数に代入さる.)

課題3

次のようなCSV形式のファイルを読込んで,表形式で表示すると共に列の平均値を表示するようにしなさい.(下記データを新たなcsvファイルとして格納し利用してもよいし,前準備で作成したcsvファイルを使ってもよい) [締切 11/14]

平均値の出力は後回しにし、まずはデータをtable表示するプログラム作りに取り組むこと。

表示例

平均値を表示する進んだ例1 空欄を含む列の処理が正しくできていない。

平均値を表示する進んだ例2

処理の手順

       $str_array = explode(",",$buffer);
  

      for($i=0; $i< count($str_array); $i++) {
        $str_array[$i]を用いた処理
      }
   

ディレクトリにあるファイルを探し出す

ディレクトリ操作関数(マニュアル参照)を用いる. opendir関数の例2を参照を参照し,次の演習を行う.

ディレクトリにおかれたファイル名を決め打ちでプログラム内に指定するので はなく,プログラムの実行段階で,存在するファイル(の一覧)を取得し,順 次開いてデータを読み込む手法を理解することにねらいがある.このような方 式であれば,CSVファイルを追加,変更するだけでページが書き換えられること になる.したがって,プログラムを書く人と,データを更 新する人の作業分担が可能になる.

課題4

下準備で置いたディレクトリにあるファイルを拾い出し,それらを演習1と同じような表形式で表示するhtmlで表示する書きなさい.ただし,ファイルごとにファイル名を明示する形式にすること. [締切 11/21]

実行例

できた人は、次章を飛ばして課題6(関数の定義と利用)を行って下さい。課題4と関連 が深い内容です。

手順

  <?php
  $dir = "./";

  // 既知のディレクトリをオープンし、その内容を読み込みます。
  if (is_dir($dir)) {  /* $dirがディレクトリであればtrue */
      $dh = opendir($dir); /* ディレクトリがあればハンドラ(ポインタ)を$dhに代入,なければfalse */
      if ($dh != false) {
          while (1) { /* $dhから一つの項目(ファイルやディレクトリ)を読み出す */
              $file = readdir($dh);
              if($file === false) break;  // 開けなければ,次へ
              echo "filename: $file : filetype: " . filetype($dir . $file) . "\n";
          }
          closedir($dh);
      }
  }
  ?>
   

    while(  ) {
       if(!preg_match('/\.csv$/',$file)) continue;
       echo $file;
    }
  
preg_match('/文字列パターン/', 文字列変数)は,正規表現関数 の一つで,文字列変数があるパターンにマッチしているかどうかを確かめ る関数である.正規表現は少々難しいが勉強してみると楽しいはずである. (授業では簡単に触れるだけにするが,次節に解説を書く.余裕のある人は先に 進んで正規表現を勉強して欲し い.奥が深いからである.)

上記の例は,末尾が".csv"でなければ,下の実行文をスキップし,次の while制御に移ることを指示するものである.

2. 上記で選択できたファイル名のファイルを順に開き,課題4のような処理を行う.

正規表現 (発展学習)2

正規表現については,http://www.ne.jp/asahi/futohen/sankaku/h083.htmを参照して欲しい.(そのほかにもWeb上にはたくさんの解説がある.)

以下は,授業では行いません.興味があれば自習してください.

Linuxには,テキストから正規表現で目的の文字列を抜き出すツールとしてgrepというのが備わっている.それを用いて練習してみよう.grepの拡張版egrepを使用する.

  less /usr/share/dict/words
(lessはテキストファイルの内容をみるツールで,上下のカーソルキーなどで位置移動できる.終了にはqをおす.)
文字の連続

     grep  "active" /usr/share/dict/words  

のようにすると"active"という文字を含む単語が抜き出される.

繰り返し
「"r"という文字があり,その後何文字かあって''active''がある」 語を表示するには,

         grep "r.*active" /usr/dict/words
  

のようにする.ドット"."は「任意の一文字」を表し,アスタリスク"*"は直前の文字の0個以上の連続を表す.このような特別な意味を表す文字を **メタキャラクタ**という.

以下にgrepで用いられる正規表現を列記する. (一般に,正規表現というと,下記の太字の項(連結,閉包,選択)を表す.)

文字列$ 行末にある「文字列」
c cという文字そのもの.cはメタキャラクタではないこと.
cd 文字cとdがこの順で現れる.( 連結: concatenation )
\c cという文字そのものcがメタキャラクタであれば,メタキャラクタとしての性質を打ち消す.
[abc] a,b,c文字のどれか.[ ] のなかに入っているものの1文字
[^abc] a,b,c以外の文字のどれか.[ ] のなかに入っているものの以外の文字
. 任意の一文字
X* Xの0個以上のならび (繰り返し(閉包): closure)
X\{m\} Xのm回の繰り返し.ただし,egrepでは,バックスラッシュなしで,X{m}とする.
\(文字列\) 「文字列」と同じだが,マッチした部分をおぼえ,あとで「¥数字」でそれを参照できる.egrepではバックスラッシュなしの( )とする.
^文字列 行の先頭にある「文字列」

これに加えて,egrepでは

文字列1|文字列2 文字列1または文字列2 (選択: union)
a+ aの1個以上の連続
(文字列) 文字列と同じ (くくり出しのかっこ) たとえば(ab)*はabの0個以上の繰り返し
[例]
パターン "egrep パターン /usr/share/dict/words"としたときに選びだされる単語
(tion|tive)$ tionあるいはtiveで終わる単語
..........$ ちょうど10文字の単語
^(.)(.).\2\1$ 5文字の回文

練習 (時間があれば試してみよう)

/usr/share/dict/wordsを材料にして,次のものを調べてみよう.各種フィルタ(tr, wc, sortなど)を組み合わせて行うこと.

  1. 末尾がtion,tiveでおわる単語それぞれの個数
  2. tion,tiveを含むがこれらが末尾にはない単語の数(とその代表例)
  3. 一番長い単語 (ヒント:すべて文字をたとえば@にしていまいtrを用いる.その後逆順に整列すれば先頭に一番長い行がくる(sortを利用).
  4. 長さ何文字の単語はいくつあるかの表.(ヒント:前問のヒント通りやった後,"uniq -c"を使う.)
  5. 一番多くの母音を含む単語.(ヒント:母音のみを「@」,それ以外の普通の文字を削除したあとで前々問のようにすれば,最も多くの母音というのは何個あるかわかる.あとはgrepで探す.
  6. 一番長い連続した母音を含む単語. (ヒント:母音のみを「@」,それ以外をすべて改行文字にした後,逆順ソートすると,最も長い母音列は何文字かわかる.あとはgrepで探す

1. 配列の内容がどのようになっているか確かめるには、 <code>print_r という関数を使う。(プログラムのテストに便利) 課題3のプログラムのなかで、次のような命令を入れてみると、 $str_array の内容をみることができる。試してみよう。

  print_r($str_array);

2. 「発展」の意味は必ずしも高度であることを意味しない.授業時間の都合で,正式課題として時間をかけないものも含む.