- 更新日: 2018年11月22日
- 公開日: 2018年11月21日
PHPでのCSVファイルの読み込みや書き出しの方法を解説!
PHPでアプリケーションを作成していると「CSVの読み書き」が必要になることが多く発生します。特に業務システムは、Excelで簡単に編集できるCSVの入出力が必須です。今回はCSVの読み書きについてサンプルコードを交えてご紹介します。
PHPでのCSVファイルの読み込みや書き出しの方法を解説!
(当記事はPHP7.1にて検証しています。)
PHPでのCSVの読み込み
CSVの読み込みは実務でも頻繁に必要になります。実はPHPにはfgetcsv()関数というCSVファイルの読み込みに便利な関数があります。
fgetcsv()関数
fgetcsv()関数の使い方を見て行きましょう。
fgetcsvは、ファイルを先頭から行を取得して、CSVフィールドを処理します。fgets()関数に動作は似ています。しかし、fgetcsv()関数は、行をCSVフォーマットのフィールドとして読み込み処理を行い、読み込んだフィールドを含む配列を返すという違いがあります。
書式
fgetcsv($handle,$length,$delimiter,$enclosure,$escape)
引数 | 属性 | パラメータ |
---|---|---|
$handle | resource | fopen()関数でオープンされたファイル |
$length(任意) | int | 分割できる最大行長を指定します。省略された場合は制限が無くなります。 |
$delimiter(任意) | string | フィールドの区切り文字を設定します。 |
$enclosure(任意) | string | フィールドの囲い込み文字を設定します。 |
$escape(任意) | string | 区切り文字や囲い込み文字などに認識されないためのエスケープ文字を設定します。 |
返り値
array型、読み込んだフィールドの内容を含む数値添字配列を返します。
サンプルコード
次にサンプルコードを見ていきます。fgetcsv.phpは、test.csvを1行ずつ読み込んで、各カラムのデータを表示します。$dataは、1行が「,(カンマ)」で分割され、「"(ダブルクォーテーション)」の囲みが取り除かれたものが配列で格納されます。なお、このコードはUTF-8のCSVをUTF-8で出力しています。SJISなどのCSVは、変換が必要なこともあるので注意しましょう。
処理を箇条書きにすると次の通りです。
- 入力ファイルをオープンする。
- 最後の行を読み込むまで1行ずつCSVファイルを各カラムごとに配列形式で読み込む。
- 入力ファイルをクローズする。
fgetcsv.php
<?php
$row = 1;
// ファイルが存在しているかチェックする
if (($handle = fopen("test.csv", "r")) !== FALSE) {
// 1行ずつfgetcsv()関数を使って読み込む
while (($data = fgetcsv($handle))) {
echo "${row}行目\n";
$row++;
foreach ($data as $value) {
echo "「${value}」\n";
}
}
fclose($handle);
}
test.csv
"yamada","CEO","Kyoto"
"shimoji","CTO","Mie"
"yamakawa","developer","Tokyo"
出力結果
$ php fgetcsv.php
1行目
「yamada」
「CEO」
「Kyoto」
2行目
「shimoji」
「CTO」
「Mie」
3行目
「yamakawa」
「developer」
「Tokyo」
それぞれ各行ごとのカラムの内容が表示されています。
PHPでのCSVの書き出し
読み込みとワンセットで行われるCSVへの書き出しもfputcsv()関数を使用することで、少ないソースコードで実装することができます。
fputcsv関数
fputcsv関数は、行をCSVにフォーマットして、ファイルポインタに書き込みます。
書式
fputcsv($handle,$fields,$delimiter,$enclosure,$escape_char)
引数 | 属性 | パラメータ |
---|---|---|
$handle | resource | fopen()関数でオープンされたファイル |
$fields(任意) | array | 出力する値の配列 |
$delimiter(任意) | string | フィールド区切り文字を指定します。 |
$enclosure(任意) | string | フィールドの囲い込み文字を指定します。 |
$escape_char(任意) | string | 区切り文字や囲い込み文字などに認識されないためのエスケープ文字を指定します。 |
返り値
int型、書き込んだ文字列の長さを返します。失敗した場合に「FALSE」を返します。
サンプルコード
fputcsv.phpの$listは、1行ごとに出力したい配列が格納されています。読み込みと同じくfopenとfcloseを行っています。
fputcsv.php
<?php
$list = array(
array('aaa', 'bbb', 'ccc', 'dddd'),
array('123', '456', '789'),
array('aaa', 'bbb')
);
$fp = fopen('file.csv', 'w');
foreach ($list as $fields) {
fputcsv($fp, $fields);
}
fclose($fp);
出力結果
aaa,bbb,ccc,dddd
123,456,789
aaa,bbb
fputcsvを使って意図しない改行を防ぐ
実はfile_put_contents()関数を使ってもCSVを作ることができます。
PHP: file_put_contents - Manual
file_put_contents()関数は、fopen、fwrite、 fcloseをせずとも関数を呼び出すだけで、ついでに処理してくれます。さらには、カンマ付きの文字列を引数に与えてあげれば、CSVを作ることもできなくはありません。
しかし、問題になるのは改行コードです。下のソースコードを見てください。$_POST['column1']には、textareaからPOSTされた改行された文字が格納されています。しかし、CSVに出力された時は、改行の前後で別のカラムとして認識されてしまいます。
file_put_contents.php
<?php
// colomun1の中で改行している。
$line = "{$_POST['column1']}, {$_POST['column2']}";
// 結果をファイルに書き出します
file_put_contents('file2.csv', $line);
file2.csv
abc
defg, column2
実際に、Excelで開いてみると、別のセルになっています。
原因は、「"(ダブルクォーテーション)」でカラムが囲まれていないので、Excelが改行文字を区切り文字として認識してしまうからです。
そこで解決策は、fputcsv()関数を使用することです。fputcsv()関数は、配列の各要素に対して、改行がある場合は「"(ダブルクォーテーション)」で囲んでくれます。次のソースコードを見てください。先ほどと同じく$_POST['column1']には、textareaからPOSTされた改行された文字が格納されています。しかし、fputcsv()関数を使うと、$_POST['column1']の文字列には、「"(ダブルクォーテーション)」で囲まれています。
fputcsv2.php
<?php
$fields = array($_POST['column1'], $_POST['column2']);
$fp = fopen('file3.csv', 'w');
fputcsv($fp, $fields);
fclose($fp);
file3.csv
"abc
defg",column2
Excelで開いた場合、セル内改行として認識されています。
CSVを書き出す時は、特別な理由がない限りfputcsv()関数を使用することで、予期しないバグを防ぐことができます。
SplFileObjectクラスでCSVを操作する
今まで見てきたCSVの操作は、fopenやfcloseでファイル操作の処理を書いていたのですが、実はもっと便利な方法があります。それは、SplFileObjectクラスを使用することです。
SPLとは「Standard PHP Library」の略称で、PHPの標準ライブラリです。SPLは、PHP5以降に含まれる標準的な問題を解決するためのインターフェイスやクラス、関数を集めたものです。その中でもファイルを読み書きするためのメソッドを実装したSplFileObjectクラスを使用します。SplFileObjectクラスは、1行ずつ読み出す、という操作をforeach構文で行うことができます。
サンプルコード
最初にインプットとアウトプット、2つのオブジェクトを作ってから処理を始めます。CSVファイルを読み込む時には、SplFileObject::setFlags()関数を使用して、インプットのオブジェクトに対してCSVを読み込むフラグをセットします。そのあとはforeach構文で、インプットのオブジェクトを回してあげれば、1行ずつ読み込むことができます。書き込みは、先ほど紹介したfputcsv()関数と同名メソッドであるSplFileObject::fputcsv()関数を使用します。
PHP: SplFileObject::fgets - Manual
PHP: SplFileObject::fgetcsv - Manual
PHP: SplFileObject::setFlags - Manual
PHP: SplFileObject::fputcsv - Manual
インプットファイルであるanimals.csvの第1カラムに動物の名前、第2カラムには動物の足の本数が格納されています。インプットファイルに対して、プログラムで作成した文章を第3カラムにセットして、新しいCSVファイルを出力します。
<?php
// インプットのCSVのオブジェクトを生成する
$inputFile = new SplFileObject("animals.csv");
// 同じくアウトプットのCSVのオブジェクトを生成する
$outputFile = new SplFileObject('output_animals.csv', 'w');
// SplFileObject::setFlags()関数はSplFileObjectによって使われるフラグをセットする
// READ_CSCはCSV 列として行を読み込む
$inputFile->setFlags(SplFileObject::READ_CSV);
// オブジェクトをforeach構文で回すだけで、fopne()関数を使わなくても1行ずつ読み込むことができる
foreach ($inputFile as $row) {
list($animal, $legs) = $row;
// 読み込んだCSVを元に文章を作成する
$sentence = sprintf("%sの足は%d本ある", $animal, $legs);
// SplFileObject::fputcsv()関数を使って、optput_animals.csvに書き出す。
$outputFile->fputcsv(array($animal, $legs, $sentence));
}
animals.csv
crocodile,4
dolphin,0
duck,2
koala,4
salmon,0
出力結果
output_animals.csv
crocodile,4,crocodileの足は4本ある
dolphin,0,dolphinの足は0本ある
duck,2,duckの足は2本ある
koala,4,koalaの足は4本ある
salmon,0,salmonの足は0本ある
ここまでSplFileObjectクラスを見てきましたが、最初に紹介したfgetcsv()関数、fputcsv()関数を使用するよりも少ない行数で書くことができました。
\Webサイト担当者としてのスキルが身に付く/
まとめ
クラスの生成ができるぐらいの知識があるのなら、SplFileObjectクラスを使用してファイル操作を行った方が時代の流れにあっています。今回は処理時間の計測やメモリ使用量について書いていませんが、もし大規模CSVファイルを扱うのであれば、SplFileObjectクラスを使うべきです。逆にPHPを触りたてで、オブジェクトの扱い方に慣れていなければ、fgetcsv()関数、fputcsv()関数からチャレンジしてみるといいでしょう。
参考文献
「パーフェクトPHP」小川雄大、柄沢聡太郎、橋口誠、著、技術評論社、2010
「PHP逆引きレシピ 第二版」鈴木憲治、山田 直明、山本義之、浅野仁、櫻井 雄大、安藤建一、著、翔泳社、2013
PHP: file_put_contents - Manual
【質問】PHPからCSVへ書き出す際の改行について - Qiita
SplFileObjectでオブジェクト指向らしいファイル操作をしてみる - Qiita
PHP: SplFileObject::fgets - Manual
PHP: SplFileObject::fgetcsv - Manual
- この記事を書いた人
- 山川竜太郎