PHPでCSV形式ファイルを読み込んで表にする

(1/1)
前回述べたように、PHPは、ローカルマシンにあるファイルと、インターネット上(URL表記)のコンテンツを同等に扱うことができる。
そこで今回は、ローカルマシン上にあるCSVファイルを読み込み、HTMLの表(TABLEタグ)に変換して画面に表示するプログラムをつくる。

(2023/04/29)タイトル変更、コメントなど追記、プログラム流れを追加

サンプル・プログラムの実行例

PHPでCSV形式ファイルを読み込んで表にする

目次

サンプル・プログラム

圧縮ファイルの内容
csv2table.phpサンプル・プログラム本体。
sample1.csvサンプルCSVファイル。
csv2table.php 更新履歴
バージョン 更新日 内容
1.6.1 2023/04/29 コメントなど追記
1.6 2021/02/06 PHP8対応
1.52 2017/07/01 setlocaleのエラー判定、PHPバージョン表示
1.51 2014/07/20 bug-fix:setlocale処理追加
1.5 2014/07/19 HTML5対応

プログラムの流れ

PHPでCSV形式ファイルを読み込んで表にする

ヒアドキュメント

  22: //HTMLヘッダを画面表示する.
  23: $encode = INTERNAL_ENCODING;
  24: $title  = TITLE;
  25: $width  = WIDTH;
  26: echo <<< EOT
  27: <!DOCTYPE html>
  28: <html lang="ja">
  29: <head>
  30: <meta charset="{$encode}">
  31: <title>{$title}</title>
  32: <meta name="author" content="studio pahoo" />
  33: <meta name="copyright" content="studio pahoo" />
  34: <meta name="ROBOTS" content="NOINDEX,NOFOLLOW" />
  35: <meta http-equiv="X-UA-Compatible" content="IE=edge">
  36: <meta name="viewport" content="width={$width},user-scalable=yes" />
  37: <style type="text/css">
  38: table, td, th {
  39:     width: {$width}px;
  40:     border-collapse: collapse;
  41:     border: 1px solid black;
  42: }
  43: </style>
  44: </head>
  45: <body>
  46: 
  47: EOT;

全体的に、入力と変換・表示を単一のプログラムファイルで処理するように工夫している。

入出力のためのHTMLタグが多いので、いちいち関数  echo  や  print  を使って出力するのは面倒である。そこで、PHPに用意されている「ヒアドキュメント」という仕組みを利用する。

たとえば、このサンプル・プログラムでは、"<<< EOT" の次の行から "EOT;" の直前行まで、そのままの形で関数  echo  に渡され、画面に表示される。
また、ヒアドキュメントの中には、PHP変数や式を埋め込むことができる。

input type="file"タグ

  63: <h2>{$title} {$version}</h2>
  64: <form method="post" action="{$myself}" enctype="multipart/form-data">
  65: ファイル選択:<input type="file" id="file" name="file" size="80" />
  66: <input type="submit" name="submit" value="変換" />
  67: </form>

ローカルマシン上のファイルを選択するダイアログを表示させるためには、HTMLの input type="file" タグを用いる。使い方は、他の input シリーズと同じである。
1つだけ注意したいのは、type="file" がHTTPサーバに送るデータは、指定されたファイルの中身も含まれているということである。もちろんバイナリ・ファイルも送ることができるので、エンコード・タイプを指定してやらなければならない。通常は、enctype="multipart/form-data"を指定する。type="file"の内容は POSTメソッド で送られ、PHP側ではスーパーグローバル変数 $_FILE で受け取ることができる。

  80: else {
  81:     //CSVファイルが存在するか(クライアントからサーバにアップされたか)
  82:     if ($_FILES['file']['size'] == 0) {
  83:         echo 'Error! - 指定したファイルが見あたりません';
  84:         exit(1);
  85:     }

送られたファイル名は、スーパーグローバル変数 $_FILES[名前]['tmp_name'] に格納される。名前は、input type="file"に付けた名前である。
また、スーパーグローバル変数 $_FILES[名前]['tmp_name'] に、送られたファイルのサイズが格納されるので、この値が 0 だったら、ファイル指定が間違っていたとしてエラーを表示する。

CSVファイルのオープン

  86:     $source_file = $_FILES['file']['tmp_name'];     //アップされたCSVファイル名
  87:     if (($infp  = fopen($source_file, 'r')) == FALSE) {
  88:         echo 'Error! - サーバ・トラブルが発生しました';
  89:         exit(1);
  90:     }

ファイルの操作は Google news の時と同様、関数  fopen  でオープンし、while ループを繰り返す。
すでに POST されたファイルをオープンするので  fopen  エラーは発生しないはずだが、念のためエラーチェックを行っている。

ロケールの設定

  92:     //CSVファイルのロケール(文字コード)を判定する.
  93:     $str = fgets($infp);
  94:     if (($enc = mb_detect_encoding($str)) == FALSE) {
  95:         echo 'Error! - 文字コードが判定できません';
  96:         exit(1);
  97:     }
  98:     //WindowsなどではFALSEでも正常に変換できるため.
  99:     if (setlocale(LC_ALL, 'ja_JP.' . $enc) == FALSE) {
 100:         $warning = '<li>Warning! - OSがロケール ' . 'ja_JP.' . $enc . ' に対応していません.</li>';
 101:     }
 102:     //念のため読み込みポインタを先頭へ戻す.
 103:     fseek($infp, 0);

このあと、関数  fgetcsv  を使ってCSVファイルを読み込むのだが、その前に関数  setlocale  を使ってロケール情報を設定しておく。
ロケール情報とは、言語や国・地域ごとに異なる単位、記号、日付、通貨などの表記規則をあらわす情報だ。
PHP4やWindows環境では指定しなくても大丈夫だが、それ以外の環境では関数  fgetcsv  で日本語が文字化けを起こすことがある。

OSによっては、シフトJISに対応するロケールが用意されていなかったりする。
そこで、関数  setlocale  の戻り値が FALSE だったら、変数 $warning に警告メッセージを記録し、最後に表示するようにした。
Linux系でロケールが用意されていない場合の対応については「PHPのfgetcsv()がsetlocaleしてもダメな時にやるべきたった一つの事」(たけまるの日記)に詳しい。
この方法でも文字化けが起きる場合は、「PHPでCSVファイルを読み込む(その2)」のプログラムをお試しいただきい。

CSVファイル読み込みと表形式への変換

 112:     //CSVファイルを1行ずつ読み込んで,カラムに分解してTABLEタグで画面表示する.
 113:     while (($csv = fgetcsv($infp, 1000, $delimiter)) !== FALSE) {
 114:         print "<tr>\n";
 115:         foreach ($csv as $key=>$val) {
 116:             if ($val == '')     $val ='&nbsp';  //デリミタ間にデータが存在しない場合は空白出力
 117:             $val = mb_convert_encoding($val, INTERNAL_ENCODING, 'auto');    //コード変換
 118:             echo "<td>{$val}</td>\n";
 119:         }
 120:         echo "</tr>\n";
 121:     }
 122:     echo "</table>";

ファイルから1行を読み込む際、関数  fgets  の代わりに、関数  fgetcsv  を用いる。関数  fgetcsv  は、

fgetcsv (fopenで戻るファイル番号, 最大読み込みバイト数, 区切り文字)

のように使う。戻り値は、区切り文字で区切られた部分文字列からなる配列である。ループ終了条件は、関数  fgets  が失敗(ファイル・エンド)するまでである。

関数  fgets  で得られた配列(表の1行に相当)を foreach で読み込み、<td> タグで囲みながら表示する。
この際、関数  mb_convert_encoding  により、入力ファイルが Shift JIS, EUC-JP, UTF-8 のいずれであっても、自動的に出力エンコード(UTF-8)に変換し、文字化けが起きないようにしている。
foreach が終わったら、<tr> タグで囲む。

なお、このプログラムは、すべての行に同じ数の区切り文字が存在している――列が結合していることはない――ことを前提としている。
ただし、「デリミタの間にデータが存在しない場合、HTMLで空欄をいれたい」という要望があったので、取り出した文字が空である場合、空白文字(&nbsp;)を出力するようにした。

このプログラムはインターネット上のCSV形式ファイルに対しても、まったく同じ手順で表形式に変換することができる。Firefox/Chrome系ブラウザであれば、「ファイルを選択」ボタンをクリックしたときに表示されるファイルダイアログに、CSVファイルのURLを入れることで変換してくれる。

参考サイト

(この項おわり)
header