RitoLabo

PHPのファイル操作クラス&関数まとめ

  • 公開:
  • カテゴリ: PHP Basics
  • タグ: PHP,SplFileObject,Basics

PHPでファイル操作を行う、例えばファイルの内容を読み込んでそれについて処理を行ったり、ファイルを作成してその中に書き込みを行ったりなどがありますが、その為にはどんな関数やクラスを用いたら処理が行えるでしょうか。

今回は、外部ライブラリは不要で利用できるPHPの基本的なファイル操作クラスや関数を用いてファイル操作を行っていきます。

アジェンダ
  1. 開発環境
  2. 基本的なファイルの読み出し
    1. 1行取得とHTMLタグ除去
  3. CSV/TSVファイルの読み込み
  4. ファイルへの書き込み
  5. CSV/TSVファイルへの書き込み
  6. file_get_contents()/file_put_contents()
  7. SplFileObjectクラス
    1. 基本的なデータ読み出し
    2. CSVファイルのデータを読み出す
    3. TSVファイルのデータを読み出す
    4. データの書き出し

開発環境

今回の開発環境に関しては以下の通りです。

  • Linux CentOS 7
  • Apache 2.4
  • PHP 7.2

デモではPHP7を使用しますが、最低でもPHP5.1以上であれば進めていけます。

基本的なファイルの読み出し

まずは最も基本的なファイルの読み込みからです。以下の関数を利用する事でファイルの読み込みを行う事が出来ます。

fopen()
ファイルもしくはURLを開く
fgets()
ファイルポインタから1行取得する
feof()
ファイルポインタがファイルの後端に達しているかどうか調べる
fclose()
オープンされたファイルポインタをクローズする

これらを利用して、以下のようにファイルを読み込み出力する事が出来ます。

// ファイルを開く
$handle = fopen("./sample1.txt", "rb");

// ループで出力
while ($data = fgets($handle)) {
var_dump($data);
}

// ファイルをクローズする
fclose($handle);

fopen()メソッドの第一引数にファイルパスを、第二引数にはアクセス形式を指定しています。読み込みなので「rb」を指定します。

アクセス形式を簡単にまとめると以下になります。

モード アクセス ファイルポインタ コンテンツ ファイル無し ファイル有り
r 読み込み 先頭
r+ 読み込み/書き出し 先頭
w 書き出し 先頭 クリア 作成する
w+ 読み込み/書き出し 先頭 クリア 作成する
a 書き出し 終端(常に追記) 作成する
a+ 読み込み/書き出し 終端(常に追記) 作成する
x 書き込み 先頭 作成する 失敗(※1)
x+ 読み込み/書き出し 先頭 作成する 失敗(※1)
c 書き込み 先頭(常に追記) 作成する
c+ 読み込み/書き出し 先頭(常に追記) 作成する
  • ※1「x」と「x+」の場合はファイルが既に存在した場合に fopen() は失敗しE_WARNINGレベルのエラーを発生させます。
  • ※PHP7より「e」モードが追加されましたがここでは割愛します。

また、アクセス形式について公式では、互換性維持のためにfopen()でファイルをオープンする時は常に「b」フラグを指定することを推奨しています。つまりは、読み込みなら「rb」書き込みなら「wb」や「ab」のように指定するのが良いです。

そして、while文でループし1行ずつ出力を行っていますが、その際の評価式は「fgets()メソッドによって1行データが取得出来たかどうか」になっています。取得できない=最後まで取得済み、もしくはエラーの場合はループが終了します。

そして最後に、fclose()メソッドでファイルをクローズします。

また、while文だけでなくfor文でループ処理を行えます。

for ($data = fgets($handle); !feof($handle); $data = fgets($handle)) {
var_dump($data);
}

1行取得し、ファイルポインタの終端に達していなければ再度取得する。という流れでループが行われます。

1行取得とHTMLタグ除去

fgets()とは別にfgetss()という関数があります。1文字違いがとてもややこしい関数ですが、このメソッドはファイルポインタから1行取り出し、コンテンツのHTMLタグを取り除きます。

例えば、以下のようなテキストファイルを用意しました。

sample.txt
<p>1</p>
<p>2</p>
<p>3</p>
<p>4</p>
<p>5</p>
<p>6</p>
<p>7</p>
<p>8</p>
<p>9</p>
<p>10</p>

Pタグで囲まれたリストですが、これをfgetss()で取り出すと結果は以下になります。

$handle = fopen("./sample.txt", "rb");

while ($data = fgetss($handle)) {
print_r($data);
}
// => 1
// 2
// 3
// 4
// 5
// 6
// 7
// 8
// 9
// 10

fclose($handle);

ちなみに、以下のようにする事で除去するHTMLタグを除外する事ができます。

$data = fgetss($handle, 100, "<p><a>");

第二引数には除去対象とするデータ長を、第三引数には除去を行わないHTMLタグを指定します。

CSV/TSVファイルの読み込み

CSVやTSVファイルを読み込む場合はfgetcsv()を使います。

// CSVの読み込み
$handle = fopen("./sample3.csv", "rb");

// 1行の情報を配列として取得する
while ($data = fgetcsv($handle)) {
print_r($data);
}

fclose($handle);

ファイルをオープンする部分は通常と同じですが、ファイルポインタから1行取得する時に、fgetcsv()を使う事で、カンマ区切りであるCSVの行データを配列として取得してくれます。

また、TSVファイルを処理したい場合は第二・第三引数を指定します。

// TSV
$handle = fopen("./sample3.tsv", "rb");

while ($data = fgetcsv($handle, 1000, "\t")) {
print_r($data);
}

fclose($handle);

第二引数には対象とする1行のデータ長を、第三引数にはデミリタとしてタブ「\t」を指定する事で、タブ区切りであるTSVファイルを読み込む事が出来ます。

尚、CSV・TSVに限らず、ファイル内コンテンツに日本語などのマルチバイト文字を含む場合でファイルがUTF-8ではない場合は文字化けするので、文字コードの変換を行ってからデータを処理します。

$data = fgetcsv($handle);

// Shift-JISのCSV/TSVファイルを処理する場合はUTF-8へ変換を行う
mb_convert_variables('UTF-8', 'sjis-win', $data);

ファイルへの書き込み

ファイルへ書き込みを行うにはfwrite()もしくはfputs()を利用します。fputs()はfwrite()のエイリアスなので、どちらを使っても同じです。

// 書き込みモードでオープン
$handle = fopen($file_path, "wb");

$num1 = mt_rand();

// 書き込み(1回目)
fwrite($handle, "{$num1}\r\n");

$num2 = mt_rand();

// 書き込み(2回目)
fwrite($handle, "{$num2}\r\n");

// クローズ
fclose($handle);

書き込みモードでファイルをオープンした後、ランダムに生成した乱数をfwrite()で書き込んでいます。これで2行書き込みが行われた事になります。

CSV/TSVファイルへの書き込み

CSVやTSVファイルへ書き込みを行う場合はfputcsv()を使います。

// 書き込みモードでオープン
$handle = fopen($file_path, "wb");

// 書き込む行データ
$record = ['apple', 'orange', 'lemon', 'plum'];

// 書き込み
fputcsv($handle, $record);

// ファイルをクローズ
fclose($handle);

書き込む行データを配列としてfputcsv()へ渡す事で書き込みが行われます。

もちろん、日本語などのマルチバイト文字を扱う場合はUTF-8からShift-JISへ文字コードの変換を行う事で文字化けは起こりません。

$record = ['庭に', '赤い', '花が', '咲いた'];

// 文字コードをShift-JISへ変換
mb_convert_variables('sjis-win', 'UTF-8', $record);

// 書き込み
fputcsv($handle, $record);

TSVファイルへの書き込みを行う場合は、fputcsv()の第三引数でタブのデミリタを指定する事でタブ区切りでの書き込みが行われます。

// 書き込みモードでオープン
$handle = fopen($file_path, "wb");

// 書き込む行データ
$record = ['apple', 'orange', 'lemon', 'plum'];

// デミリタをタブに指定して書き込む
fputcsv($handle, $record, "\t");

// ファイルをクローズ
fclose($handle);

file_get_contents()/file_put_contents()

ローカルやリモートのファイル内容を文字列として読み込んだり書き込んだりするには、file_get_contents()file_put_contents()関数を使います。

ファイルの内容を文字列としてファイルに読み込むには、file_get_contents関数を使います。

$file = file_get_contents("./sample.txt");

echo $file;
// => 1 2 3 4 5 6 7 8 9 10

指定したファイルの内容を全て文字列として読み込んでいます。

反対に、データをファイルへ書き出すには、file_put_contents()関数を使います。

$file_path = "./sample.txt";

// ファイルに書き込む
file_put_contents($file_path, "test write");

SplFileObjectクラス

SplFileObjectクラスは、ファイルをオブジェクト指向で扱う為のクラスで、SPL(Standard PHP Library)拡張モジュールのファイル操作クラスです。

ちなみにSPL(Standard PHP Library)は、標準的な処理の為のインターフェイスやクラスを集めた拡張モジュールです。PHP 5.0.0以降はデフォルトで組み込まれています。

基本的なデータ読み出し

まずは、基本的なテキストファイルからのデータ読み出しです。

$file = new SplFileObject("./sample.txt");

foreach ($file as $line) {
print_r($line);
}
// => 1 2 3 4 5 6 7 8 9 10

コンストラクタにファイルパスを渡し、SplFileObjectクラスをインスタンス化します。これだけであとはforearchでループさせればデータを1行ずつ読み出す事が出来ます。

CSVファイルのデータを読み出す

CSVファイルのデータを読み出すには以下のように記述します。

$file = new SplFileObject("./sample.csv");

$file->setFlags(
SplFileObject::READ_CSV |
SplFileObject::READ_AHEAD |
SplFileObject::SKIP_EMPTY |
SplFileObject::DROP_NEW_LINE
);

foreach ($file as $line) {
// マルチバイトを扱う場合は文字コードをUTF-8へ変換する
mb_convert_variables('UTF-8', 'sjis-win', $line);
echo'<pre>'; print_r($line); echo'</pre>';
}

setFlags()メソッドでCSVを読み出す為の設定を行っています。引数に渡しているのはSplFileObjectクラスの定数です。

READ_CSV
CSV 列として行を読み込む
READ_AHEAD
先読み/巻き戻しで読み出す
SKIP_EMPTY
空行は読み飛ばす
DROP_NEW_LINE
行末の改行を読み飛ばす

フラグセットを行ったら、あとは通常通りループで回す事で行単位で配列を取得できます。

Array
(
[0] => 1月1日
[1] => 1月2日
[2] => 1月3日
[3] => 1月4日
[4] => 1月5日
)
Array
(
[0] => 2月1日
[1] => 2月2日
[2] => 2月3日
[3] => 2月4日
[4] => 2月5日
)
Array
(
[0] => 3月1日
[1] => 3月2日
[2] => 3月3日
[3] => 3月4日
[4] => 3月5日
)

TSVファイルのデータを読み出す

CSVファイルはカンマ区切りですが、TSVファイルはタブ区切りです。基本的な記述はCSVと同じですが、TSVファイルを読み出す時はsetCsvControl()メソッドでデミリタをタブに切り替えるだけです。

$file = new SplFileObject("./sample.tsv");

$file->setFlags(
SplFileObject::READ_CSV |
SplFileObject::READ_AHEAD |
SplFileObject::SKIP_EMPTY |
SplFileObject::DROP_NEW_LINE
);

// デミリタをタブ指定へ変更し、TSV処理へ変更
$file->setCsvControl("\t");

foreach ($file as $line) {
// マルチバイトを扱う場合は文字コードをUTF-8へ変換する
mb_convert_variables('UTF-8', 'sjis-win', $line);
print_r($line);
}

データの書き出し

SplFileObjectクラスでファイルにデータを書き出すには以下のようにします。

$file_path = "./write.txt";
$file = new SplFileObject($file_path, "wb");

$file->fwrite("test data");

コンストラクタの第二引数に書き込みのアクセス形式を渡してインスタンス化を行います。

あとはfwrite()メソッドで書き込みを行います。

CSVファイルへ書き出すには、fputcsv()メソッドを使います。

$file_path = "./write.csv";
$file = new SplFileObject($file_path, "wb");

$data = [
["1", "1-1", "1-2", "1-3", "1-4"],
["2", "2-1", "2-2", "2-3", "2-4"],
["3", "3-1", "3-2", "3-3", "3-4"],
];

foreach ($data as $d) {
$file->fputcsv($d);
}

マルチバイトを扱う場合は文字コードをUTF-8へ変換します。

$file_path = "./write.csv";
$file = new SplFileObject($file_path, "w");

$data = [
["1", "おはよう", "こんにちは", "こんばんは", "おやすみ"],
["2", "庭に", "赤い", "花が", "咲いた"],
["3", "Good morning", "Hello", "Good evening", "Good night"],
];

foreach ($data as $line) {
mb_convert_variables('sjis-win', 'UTF-8', $line);
$file->fputcsv($line);
}

TSVファイルへの書き出しも、基本はCSVと同じです。読み込みと同じように、setCsvControl()メソッドでデミリタをタブに切り替えて書き込みを行います。

$file_path = "./write.tsv";
$file = new SplFileObject($file_path, "wb");

$file->setCsvControl("\t");

$data = [
["1", "1-1", "1-2", "1-3", "1-4"],
["2", "2-1", "2-2", "2-3", "2-4"],
["3", "3-1", "3-2", "3-3", "3-4"],
];

foreach ($data as $d) {
$file->fputcsv($d);
}

まとめ

ファイル操作はPHPの中でも基本的な処理ですが、実務で実装する場合にはデータを書き出したり読み出したりと、重要な機能になります。適切な処理をお行い、安定した機能を実装できるように基本をしっかり押さえておきましょう。