🐘

PHPでCSVを扱うならleague/csvが便利【基本的な使い方まとめ】

2020/02/22に公開

こんにちは、たつきちです。

エンジニア歴12年ぐらいで今はベンチャー企業のCTOをしています。

この記事では、PHPでCSVデータを扱うためのライブラリ league/csv をご紹介します。

ぜひ最後までお付き合いください。

league/csvとは

league/csv は、PHPでCSVデータを簡単かつ柔軟に扱うためのライブラリです。

従来のように SplFileObject::fgetcsv とかを使って低レイヤーのコードを自力で書かなくても、いい感じでCSVデータを抽象化してくれます。

例えば以下のような簡潔なコードでCSVファイルを扱うことができます👍

CSVファイル

PHPコード

<?php
use League\Csv\Reader;

$csv = Reader::createFromPath('/path/to/your/csv/file.csv', 'r');
$csv->setHeaderOffset(0);

foreach ($csv as $row) {
    var_dump($row);
}

// array (size=3)
//   'id' => string '1' (length=1)
//   'name' => string 'Tommy Yount' (length=11)
//   'email' => string 'tommy_yount@gmail.com' (length=21)
// array (size=3)
//   'id' => string '2' (length=1)
//   'name' => string 'Hye Panter' (length=10)
//   'email' => string 'hye_panter@gmail.com' (length=20)
// array (size=3)
//   'id' => string '3' (length=1)
//   'name' => string 'Keva Bandy' (length=10)
//   'email' => string 'keva_bandy@gmail.com' (length=20)

基本的な使い方

CSVファイルを読み込む

use League\Csv\Reader;

$csv = Reader::createFromPath('/path/to/your/csv/file.csv', 'r');
$csv->setHeaderOffset(0);

foreach ($csv as $row) {
    var_dump($row);
}

CSVファイルを途中から読み込む

use League\Csv\Reader;
use League\Csv\Statement;

$csv = Reader::createFromPath('/path/to/your/csv/file.csv', 'r');
$csv->setHeaderOffset(0);

// 2行目(0始まり)から1行だけを読み込む
$stmt = (new Statement())
    ->offset(2)
    ->limit(1)
;
$rows = $stmt->process($csv);

foreach ($rows as $row) {
    var_dump($row);
}

// array (size=3)
//   'id' => string '3' (length=1)
//   'name' => string 'Keva Bandy' (length=10)
//   'email' => string 'keva_bandy@gmail.com' (length=20)

SJIS-winのCSVファイルをUTF-8に変換して読み込む

use League\Csv\CharsetConverter;
use League\Csv\Reader;

$csv = Reader::createFromPath('/path/to/your/csv/file.csv', 'r');
$csv->setHeaderOffset(0);

// 文字コードを変換
CharsetConverter::addTo($csv, 'SJIS-win', 'UTF-8');

foreach ($csv as $row) {
    var_dump($row);
}

参考:https://csv.thephpleague.com/9.0/converter/charset/

タブ区切りのCSV(TSV)ファイルを読み込む

use League\Csv\Reader;

$csv = Reader::createFromPath('/path/to/your/csv/file.csv', 'r');
$csv->setHeaderOffset(0);

// デリミタをタブ文字に変更
$csv->setDelimiter("\t");

foreach ($csv as $row) {
    var_dump($row);
}

注意点(僕が無駄にハマったところ)

上記の例でCSVデータを1行ずつ出力した際、

array (size=3)
  'id' => string '1' (length=1)
  'name' => string 'Tommy Yount' (length=11)
  'email' => string 'tommy_yount@gmail.com' (length=21)

このように

[
    '列名' => '値',
    '列名' => '値',
    '列名' => '値',
]

という形の配列として出力されていました。

しかし、例えば以下のように $csv->setHeaderOffset(0); をせずに同じ処理を実行すると…

use League\Csv\Reader;

$csv = Reader::createFromPath('/path/to/your/csv/file.csv', 'r');
// $csv->setHeaderOffset(0);

foreach ($csv as $row) {
    var_dump($row);
}

以下のように出力されます。

array (size=3)
  0 => string 'id' (length=2)
  1 => string 'name' (length=4)
  2 => string 'email' (length=5)
array (size=3)
  0 => string '1' (length=1)
  1 => string 'Tommy Yount' (length=11)
  2 => string 'tommy_yount@gmail.com' (length=21)
array (size=3)
  0 => string '2' (length=1)
  1 => string 'Hye Panter' (length=10)
  2 => string 'hye_panter@gmail.com' (length=20)
array (size=3)
  0 => string '3' (length=1)
  1 => string 'Keva Bandy' (length=10)
  2 => string 'keva_bandy@gmail.com' (length=20)

列名を添え字とする連想配列ではなく、CSVデータを物理的に配列化したものが出力されています。

$csv->setHeaderOffset(0); が、 「CSVの何行目(0始まり)がヘッダー行なのか」を教える 処理なので、これを実行しないとヘッダーの内容が分からないのです。

僕は以前これを理解していなくて無駄にハマったので、皆さんはお気をつけください😅

まとめ

  • PHPでCSVを扱うならleague/csvが便利
  • 巨大CSVファイルを読み込むときもストリーム経由なのでメモリを大量消費しなくてありがたい
  • $csv->setHeaderOffset({ヘッダー行の位置}); を実行するのとしないのとでCSVデータの配列の形式が異なるので要注意
GitHubで編集を提案

Discussion