LoginSignup
17
28

More than 3 years have passed since last update.

PHPでCSVとTSV

Last updated at Posted at 2018-02-21

最近はエクセルばかりでCSVは使っていませんでした。
今まではfgetcsvを使って読み込んでいましたが、久しぶりに使おうと調べてみたらSplFileObjectSplTempFileObjectを使うと圧倒的に楽ということを知りましたのでメモ。
(通常のファイル作成にも使えます)

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"
}

但し、SplTempFileObject

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

17
28
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
28