12.ファイル操作
PHPのファイルは、サーバー側に各種形式で保存ができます。
基本的には、テキストファイルの操作になりますが、ライブラリを利用することで
PDFやWord,Exce、画像(JPEG,PING)lなどのファイル形式で保存できるようになります。
1)ファイル操作の基本
a.ファイルオープンの書式
ファイル操作を行う場合は、ファイルを開く(オープン)処理が、必要になります。書式は、以下のようになります。
<書式>
ファイルリソース= fopen("パスを含めたファイル名", "オープンモード");
<例>$fp=fopen("data/address.txt","a");
ファイルリソースとは、ファイルを開いている間、そのファイルに継続的にアクセスできるように開いた
ファイルごとに一意の値のことで、fopen()関数の戻り値がその値になります。
ファイルのオープンモードは以下のようになります。
モード |
処理 |
"r" |
読み取り専用(ファイルがなければエラー) |
"r+" |
読み取りと書き込み(ファイルがなければエラー) |
"w" |
書き出し専用(ファイルがなければ新規作成、上書き) |
"w+" |
書き出しと読み込み(ファイルがなければ新規作成、上書き) |
"x" |
書き込み専用(同じファイルが存在すればエラー) |
"x+" |
書き込みと読み込み(同じファイルが存在すればエラー) |
"a" |
追記専用(ファイルがなければ新規作成、追記) |
"a+" |
追記と読み込み(ファイルがなければ新規作成、追記) |
b.ファイルクローズの書式
ファイル処理が終了するとファイルを閉じる(クローズ)処理が必要になります。書式は以下のようになります。
<書式>fclose(ファイルリソース); ファイルリソースは、fopen()時のファイルリソースを指定します。
<例>fclose($fp);
c.ファイルの読み込み
現在のファイルポインタの位置から読み込みます。
改行文字(\r\n)を含めた行単位で読み込みます。
<書式>格納する変数=fgets(ファイルリソース[,読み込むバイト数]);
<例>$dat=fgets($fp);
・文字単位で読み込む関数
<書式>格納する変数=fgetc(ファイルリソース);
<例>$dat=fgetc($fp);
d.ファイルへの書き込み
データをファイルオープンモードに従ってファイルに書き込みます。
・行単位で書き込む関数
データを書き込むとき、改行コード(\r\n)を付加しないので、必要に応じて付加する必要があります。
<書式>fputs(ファイルリソース, "書き込む文字列"[, 書き込むバイト数]);
<例>fputs($fp,$dat);
・fputs()実行後の処理
fputs()を実行しても実際にはバッファに書き込まれるだけでファイルには保存されていません。
実際にファイルに書き込むためには、以下の関数を実行する必要があります。
<書式>fflush(ファイルリソース);
<例>fflush($fp);
e.ファイルの終わりを知る方法
ファイルの終わりを知るには、以下の関数を使います。
<書式>feof(ファイルリソース);
<例>feof($fp);
ファイルの終端までファイルポインタが移動したときにTRUEを返します。
それ以外のときは、FALSEを返します。
f.ファイルポインタを移動させる
ファイルポインタを移動させる関数は、任意にファイルの行位置を指定することができます。
<書式>fseek(ファイルリソース,シーク量[,シーク基準]);
シーク量:シーク(移動)する量をバイトで指定します。
1を指定すると次の行へファイルポインタを移動させます。
-1を指定すると一つ前の行へファイルポインタを移動させます。
シーク基準:カレントファイルポインタを設定します。省略するとファイルの先頭がカレントファイル
ポインタになります。
SEEK_SETを指定すると、ファイルの先頭がカレントファイルポインタになります。
SEEK_CURを指定すると、カレントファイルポインタからの相対位置になります。
SEEK_ENDを指定すると、ファイルの最後がカレントファイルポインタになります。
g.テキストファイルの行数を調べる方法
テキストファイルの行数や配列のインデックス数(要素数)を調べる関数に以下のものがあります。
<書式>count(配列名または、ファイルリソース);
<サンプル:配列>
$a[0]=10;
$a[1]=20;
count($a); //この場合、count()関数の戻り値は、「2」になります。
<サンプル:ファイル>
$fp=fopen("data/address.txt","r");
count($fp); //ファイル「address.txt」の行数が10行であれば、count()関数の戻り値は、「10」になります。
2)フォームから送信されたデータをCSVファイルで保存する。
CSVファイルとは、文字列をある特定の文字で区切ったファイル形式のことで
この区切り文字として「,」が
よく使われます。しかし、文字列の中にデータとして「,」が存在する場合は、区切り文字として「,」は使えませ
ん。
以下に「,」区切りの文字列の例を示します。
"山田太郎,632-0015,大阪市・・・,06-0000-0000,abc@abc.com"
次のプログラムを実行してみてください。サーバーにファイル名「address.csv」のファイルが
作られデータが書き込まれています。
CSVファイルの拡張子は、何でもかまいませんが、一般的には、[.csv]にします。
このようにすると、Excelで直接開くことができます。
<ファイル名:fileSave.htm>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>ファイル保存データ送信フォーム</title>
</head>
<body>
<form method="post" action="fileSave.php">
氏名:<input type="text" name="fName" /><br />
郵便番号:<input type="text" name="fZip" /><br />
住所:<input name="fAddress" type="text" size="50" /><br />
E-Mail:<input name="fMail" type="text" id="fMail" size="30" />
<input type="submit" name="button" id="button" value="送信" />
</form>
</body>
</html>
<ファイル名:fileSave.php>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>ファイルをCSVで保存する</title>
</head>
<body>
<?php
$sName=$_POST['fName'];
$sZip=$_POST['fZip'];
$sAddress=$_POST['fAddress'];
$sMail=$_POST['fMail'];
$dat=$sName.",".$sZip.",".$sAddress.",".$sMail;
$fp=fopen("address.csv","a");
fputs($fp,$dat."\r\n");
fflush($fp);
fclose($fp);
?>
</body>
</html>
<説明>
1.$dat=$sName.",".$sZip.",".$sAddress.",".$sMail; は、CSV形式の文字列を作っています。
2.$fp=fopen("address.csv","a");は、使用するファイルを開いて、ファイルリソースを$fpに代入しています。
この場合、address.csvファイルを追記専用(ファイルがなければ新規作成、追記)でオープンしています。
3.fputs($fp,$dat."\r\n"); は、fputs()が改行コードを付加しないので付加して保存しています。
"\r\n"は、Windows(Shift-JIS)の改行コードを表すキーワードです。
4.fflush($fp); は、fputs()がバッファに書き込むため実際のファイルに書き込むために実行しています。
5.fclose($fp);は、
fopen()関数で開いたファイルを閉じている。ファイルは、開いたままにしておくといけな
いので必ず閉じるようにします。このとき、引数は、fopen()関数で取得したファイルリソースを指定します。
しかし、上記のプログラムでは、Excelで表示しても2バイトコードが文字化けしています。
これは、ExcelがShift-JISコードになっているためです。
このプログラムのファイルがUTF-8になっているのでPHPはデフォルトでUTF-8コードで保存してしまします。
そこで明確にShift-JISで保存させるためには、以下のようにプログラムを変更します。
<ファイル名:fileSave.php>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>ファイルをCSVで保存する</title>
</head>
<body>
<?php
$sName=$_POST['fName'];
$sZip=$_POST['fZip'];
$sAddress=$_POST['fAddress'];
$sMail=$_POST['fMail'];
$dat=$sName.",".$sZip.",".$sAddress.",".$sMail;
mb_language("Japanese");
$dat=mb_convert_encoding($dat,"Shift-JIS", "UTF-8");
$fp=fopen("address.csv","a");
fputs($fp,$dat."\r\n");
fflush($fp);
fclose($fp);
?>
</body>
</html>
<説明>
1.mb_language("Japanese"); は、日本語の使用を設定しています。(php.iniファイルで日本語の設定がされている場合は、必要ありません。)
2.$dat=mb_convert_encoding($dat,"Shift-JIS", "UTF-8"); は、UTF-8のコードをShift-JISに変換しています。
これは、Excelが標準で、Shift-JISを使っているために変換しています。
3)CSVファイルのデータをテーブルを使って表示する。
a.HTMLにPHP変数を埋め込む場合
<ファイル名:csvDisp1.php>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>ファイルの読み込み</title>
</head>
<body>
<table border="1">
<tr>
<th>氏名</th><th>郵便番号</th><th>住所</th><th>E-Mail</th>
</tr>
<?php
mb_language("Japanese");
$fp=fopen("address.csv","r");
while(feof($fp)!=TRUE){
$dat=fgets($fp);
$dat=str_replace("\r|\n","",$dat);
if($dat!=""){
$dat=mb_convert_encoding($dat,"UTF-8", "Shift-JIS");
$datArray=explode(",",$dat);
?>
<tr>
<?php
for($i=0;$i<count($datArray);$i++){
?>
<td><?=$datArray[$i]?></td>
<?php
}
}
?>
</tr>
<?php
}
fclose($fp);
?>
</table>
</body>
</html>
<説明>
1.while(feof($fp)!=TRUE)は、ファイルの終わりが来るまで繰返しをしています。
このループは、テキストファイル(CSVファイル)の行に対して行っています。
2.$dat=str_replace("\r|\n","",$dat); は、fgets()が改行コードを取り除かないので取り除く処理をしています。
3.if($dat!="") は、「fileSave.php」のプログラムで改行コードを付加して、追記(アペンド)保存しているために、
ファイルの最後の行は、空白行(空文字)になっています。このまま、ブラウザに表示しようとすると空文字として、
表示してしまうので、この空文字を除外しています。
メモ帳なので文字を入力した後、改行キー(Enterキー)を押したときにカーソルが次の行に行きます。
これが、空白行(空文字)になります。
この処理をしなくてもいいようにするためには、ファイル保存するときに最後の行だけ改行コード(\r\n)
を付加しないようにすれば可能になります。
4.$datArray=explode(",",$dat); は、「,」区切りの文字列を「,」で区切って「$datArray」配列に代入しています。
split()関数の戻り値を変数に代入する場合、自動的に配列変数に宣言されます。
インデックス値は自動的に「0」からになるので、$datArray[0]から順に代入されます。
5.for($i=0;$i<count($datArray);$i++)は、配列$datArray[]のインデックス数(要素数)をcount()関数を
使って調べて、その配列のインデックス数分だけループさせるようにしています。
4.で説明したように、配列のインデックス値が自動的に「0」から始まるので$i=0~count($datArray)-1に
なるまでループさせています。
このループは、読み込んだ行の列要素が入っている配列($datArray[])に対して行っています。
b.PHPプログラムにHTMLコードを埋め込む場合
<ファイル名csvDisp2.php>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>ファイルの読み込み</title>
</head>
<body>
<table border="1">
<tr>
<th>氏名</th><th>郵便番号</th><th>住所</th><th>E-Mail</th>
</tr>
<?php
mb_language("Japanese");
$fp=fopen("address.csv","r");
while(feof($fp)!=TRUE){
$dat=fgets($fp);
$dat=str_replace("\r|\n","",$dat);
if($dat!=""){
$dat=mb_convert_encoding($dat,"UTF-8", "Shift-JIS");
$datArray=explode(",",$dat);
echo "<tr>\n";
for($i=0;$i<count($datArray);$i++){
echo "<td>${datArray[$i]}</td>\n";
}
}
echo "</tr>\n";
}
fclose($fp);
?>
</table>
</body>
</html>
<課題>
1.2)のプログラムでデータを受け取った旨を表示するようにしてください。
2.2)のプログラムでフォームからデータを送信するとき未入力のテキストフィールドが存在するときは、再入力ができるようにしてください。
このとき、すでに入力されているデータは、テキストフィールドに表示できるようにしてください。
<1.2.の答え>
3.出来上がったCSVファイルをファイルの末尾から先頭に向かって表示するようにしてください。
3)のaまたは、3)のbのプログラムを変更してください。
<ヒント>
ファイルの行数をcount()関数を使って調べた結果、fseek()関数を使ってファイルの末尾に移動して、
ファイルの前方に向かって、ファイルポインタを移動しながら処理します。このときループの回数には、
count()関数を使って取得した値を使います。