最近はエクセルばかりでCSVは使っていませんでした。
今まではfgetcsvを使って読み込んでいましたが、久しぶりに使おうと調べてみたらSplFileObjectやSplTempFileObjectを使うと圧倒的に楽ということを知りましたのでメモ。
(通常のファイル作成にも使えます)
CSV
読み込み
read.csv
headA,headB,headC
dataA,"data,B","data""C"
$read_file_path = __DIR__.'/read.csv';
$file = new \SplFileObject($read_file_path, 'r');
$file->setFlags(\SplFileObject::READ_CSV | \SplFileObject::SKIP_EMPTY | \SplFileObject::READ_AHEAD);// 空行無視はSKIP_EMPTYとREAD_AHEADを共に設定する
foreach ($file as $row)
{
var_dump($row);
}
dd($file);// Laravelの出力用ヘルパ関数
出力結果
array(3) { [0]=> string(8) "headA" [1]=> string(5) "headB" [2]=> string(5) "headC" }
array(3) { [0]=> string(5) "dataA" [1]=> string(6) "data,B" [2]=> string(6) "data"C" }
SplFileObject {#63632 ▼
path: "/home/public/app/app/Http/Controllers"
filename: "read.csv"
basename: "read.csv"
pathname: "/home/public/app/app/Http/Controllers/read.csv"
extension: "csv"
realPath: "/home/public/app/app/Http/Controllers/read.csv"
aTime: 2018-02-21 20:15:37
mTime: 2018-02-21 20:15:37
cTime: 2018-02-21 20:15:37
inode: 4407
size: 48
perms: 0100777
owner: 1000
group: 50
type: "file"
writable: true
readable: true
executable: true
file: true
dir: false
link: false
csvControl: array:3 [▶]
flags: READ_AHEAD|SKIP_EMPTY|READ_CSV
maxLineLen: 0
fstat: array:26 [▶]
eof: false
key: 0
}
書き込み
$save_file_path = __DIR__.'/save.csv';
$file = new \SplFileObject($save_file_path, 'w');// ファイルは作成してくれます。
$data = [];
$data[] = ['headA', 'headB', 'headC'];
$data[] = ['dataA', 'data,B', 'data"C'];
foreach ($data as $row) {
$file->fputcsv($row);
}
dd($file);// Laravelの出力用ヘルパ関数
save.csv
headA,headB,headC
dataA,"data,B","data""C"
出力結果
SplFileObject {#63653 ▼
path: "/home/public/app/app/Http/Controllers"
filename: "save.csv"
basename: "save.csv"
pathname: "/home/public/app/app/Http/Controllers/save.csv"
extension: "csv"
realPath: "/home/public/app/app/Http/Controllers/save.csv"
aTime: 2018-02-21 20:47:28
mTime: 2018-02-21 20:47:28
cTime: 2018-02-21 20:47:28
inode: 4422
size: 44
perms: 0100777
owner: 1000
group: 50
type: "file"
writable: true
readable: true
executable: true
file: true
dir: false
link: false
csvControl: array:3 [▶]
flags: 0
maxLineLen: 0
fstat: array:26 [▶]
eof: false
key: 0
}
TSV
読み込み
read.tsv
headA headB headC
dataA "data B" "data""C"
$read_file_path = __DIR__.'/read.tsv';
$file = new \SplFileObject($read_file_path, 'r');
$file->setFlags(\SplFileObject::READ_CSV | \SplFileObject::SKIP_EMPTY | \SplFileObject::READ_AHEAD);// 空行無視はSKIP_EMPTYとREAD_AHEADを共に設定する
$file->setCsvControl("\t");
foreach ($file as $row)
{
var_dump($row);
}
dd($file);// Laravelの出力用ヘルパ関数
出力結果
array(3) { [0]=> string(5) "headA" [1]=> string(5) "headB" [2]=> string(5) "headC" }
array(3) { [0]=> string(5) "dataA" [1]=> string(6) "data B" [2]=> string(6) "data"C" }
SplFileObject {#4248 ▼
path: "/home/public/app/app/Http/Controllers"
filename: "read.tsv"
basename: "read.tsv"
pathname: "/home/public/app/app/Http/Controllers/read.tsv"
extension: "tsv"
realPath: "/home/public/app/app/Http/Controllers/read.tsv"
aTime: 2018-03-01 23:40:18
mTime: 2018-03-01 23:37:06
cTime: 2018-03-01 23:37:06
inode: 751
size: 43
perms: 0100777
owner: 1000
group: 50
type: "file"
writable: true
readable: true
executable: true
file: true
dir: false
link: false
csvControl: array:3 [▶]
flags: READ_AHEAD|SKIP_EMPTY|READ_CSV
maxLineLen: 0
fstat: array:26 [▶]
eof: true
key: 2
}
書き込み
$save_file_path = __DIR__.'/save.tsv';
$file = new \SplFileObject($save_file_path, 'w');// ファイルは作成してくれます。
$file->setCsvControl("\t");
$data = [];
$data[] = ['headA', 'headB', 'headC'];
$data[] = ['dataA', "data\tB", 'data"C'];
foreach ($data as $row) {
$file->fputcsv($row);
}
dd($file);// Laravelの出力用ヘルパ関数
save.tsv
headA headB headC
dataA "data B" "data""C"
出力結果
SplFileObject {#4261 ▼
path: "/home/public/app/app/Http/Controllers"
filename: "save.tsv"
basename: "save.tsv"
pathname: "/home/public/app/app/Http/Controllers/save.tsv"
extension: "tsv"
realPath: "/home/public/app/app/Http/Controllers/save.tsv"
aTime: 2018-03-01 23:56:42
mTime: 2018-03-01 23:56:42
cTime: 2018-03-01 23:56:42
inode: 869
size: 43
perms: 0100777
owner: 1000
group: 50
type: "file"
writable: true
readable: true
executable: true
file: true
dir: false
link: false
csvControl: array:3 [▶]
flags: 0
maxLineLen: 0
fstat: array:26 [▶]
eof: false
key: 0
}
CSVやTSV形式の文字列を配列に変換する
一時的なファイルオブジェクトに追記して取得を行えばよい
$csvString = 'dataA,"data,B","data""C"';
$tempFile = new \SplTempFileObject();
$tempFile->setFlags(\SplFileObject::READ_CSV | \SplFileObject::SKIP_EMPTY | \SplFileObject::READ_AHEAD);
$tempFile->fwrite($csvString);// そのまま書き込む
$tempFile->rewind();// fpを先頭へ
foreach ($tempFile as $row)
{
var_dump($row);
}
array(3) {
[0]=>
string(5) "dataA"
[1]=>
string(6) "data,B"
[2]=>
string(6) "data"C"
}
result | |
---|---|
getPathname() | php://temp |
getRealPath() | false |
だったのでメモリを使っているようなので容量が大きくなりそうな場合は/tmp
を使った方がよさそう。
$testFilePath = tempnam(sys_get_temp_dir(), 'test_');
$testFile = new \SplFileObject($testFilePath, 'w+');
$tempFile->fwrite($csvString);
$tempFile->rewind();
foreach ($tempFile as $row)
{
var_dump($row);
}
注意
Text file busy
になるので使用し終わったらunset($file)
等で開放しておいた方が良さそうです。
参考
SplFileObject::fgetcsv
SplFileObject::fputcsv
How to remove the extra line when using PHP SplFileObject and READ_CSV flag?
SplFileObject::setCsvControl