【PHP】Goodby CSVを使って高メモリ効率でCSVをパースする

# Goodby CSVってなに
メモリ効率が非常に良い、CSVをインポート/エクスポートするためのPHPライブラリです。
github : [Goodby CSV](https://github.com/goodby/csv)

個人のアプリケーションなどでの利用ではあまりメモリ効率を考える必要がないかもしれませんが、何百万、何千万行となるような大規模なプロジェクトではCSVの処理に莫大なメモリを使用することになります。

Goodby CSVではCSVのすべての行を一気に読み込むことは無く、一行ずつコールバック関数で処理しています。

また、SJIS-win、EUC-JP、UTF-8などもサポートしているため仕様に柔軟に対応できます。

# 要件
・PHP 5.3.2 以上
・mbstring

# インストール
Composerを利用しますので、プロジェクトフォルダ内にcomposerをインストールします。

```
curl -s http://getcomposer.org/installer | php
```

プロジェクトディレクトリのrootにcomposer.jsonを作成して、下記を記載します。

```json
{
    "require": {
        "goodby/csv": "*"
    }
}
```

インストールします。

```
php composer.phar install
```

# 使い方
## configの設定
インポート時

```php
use Goodby\CSV\Import\Standard\LexerConfig;

$config = new LexerConfig();
$config
    ->setDelimiter("\t") // Customize delimiter. Default value is comma(,)
    ->setEnclosure("'")  // Customize enclosure. Default value is double quotation(")
    ->setEscape("\\")    // Customize escape character. Default value is backslash(\)
    ->setToCharset('UTF-8') // Customize target encoding. Default value is null, no converting.
    ->setFromCharset('SJIS-win') // Customize CSV file encoding. Default value is null.
;
```

エクスポート時

```php
use Goodby\CSV\Export\Standard\ExporterConfig;

$config = new ExporterConfig();
$config
    ->setDelimiter("\t") // Customize delimiter. Default value is comma(,)
    ->setEnclosure("'")  // Customize enclosure. Default value is double quotation(")
    ->setEscape("\\")    // Customize escape character. Default value is backslash(\)
    ->setToCharset('SJIS-win') // Customize file encoding. Default value is null, no converting.
    ->setFromCharset('UTF-8') // Customize source encoding. Default value is null.
    ->setFileMode(CsvFileObject::FILE_MODE_WRITE) // Customize file mode and choose either write or append. Default value is write ('w'). See fopen() php docs
;
```

区切り文字、括り文字、エスケープ文字、文字コード、ファイルモードの設定などが出来ます。

##使用例：PDOによるCSVデータをDBへインポート
インポートするCSV

```users.csv
1,tom,tomcruiseikemen@example.com
2,taro,taro@example.com
3,hanako,hanako@eample.com
```

hogeというDBにusersというテーブルがあるとします。

下記を実行します。

```CsvImporter.php
use Goodby\CSV\Import\Standard\Lexer;
use Goodby\CSV\Import\Standard\Interpreter;
use Goodby\CSV\Import\Standard\LexerConfig;

require_once("vendor/autoload.php");

//DBの設定(postgreSQLの場合)
$pdo = new PDO('pgsql:host=localhost;dbname=hoge', 'postgres', '');

//configの設定
$config = new LexerConfig();
$lexer = new Lexer($config);

$interpreter = new Interpreter();

$interpreter->addObserver(function(array $columns) use ($pdo) {
    //データに処理を入れるときはこのあたりに
    $stmt = $pdo->prepare('INSERT INTO users (id, name, email) VALUES (?, ?, ?)');
    $stmt->execute($columns);
});

//読み込むCSVファイル
$lexer->parse('users.csv', $interpreter);
```

こうなります。

<img width="588" alt="スクリーンショット 2017-09-13 23.45.27.png" src="https://qiita-image-store.s3.amazonaws.com/0/124843/40b97b1c-32f1-6b0a-10ef-26062e338b44.png">


## 使用例：PDOによるDBからデータをCSVにエクスポート
下記を実行します。

```CsvExporter.php
use Goodby\CSV\Export\Standard\Exporter;
use Goodby\CSV\Export\Standard\ExporterConfig;
use Goodby\CSV\Export\Standard\CsvFileObject;
use Goodby\CSV\Export\Standard\Collection\PdoCollection;
use Goodby\CSV\Export\Standard\Collection\CallbackCollection;

require_once("vendor/autoload.php");

//DBの設定(postgreSQLの場合)
$pdo = new PDO('pgsql:host=localhost;dbname=hoge', 'postgres', '');

$stmt = $pdo->prepare("SELECT * FROM users");
$stmt->execute();

$collection = new CallbackCollection(new PdoCollection($stmt), function($row) {
    //ここにデータの処理など
    $row['name'] = strtoupper($row['name']);

    return $row;
});

//configの設定 SJISで出力する
$config = new ExporterConfig();
$config->setToCharset('SJIS-win');

//エクスポート
$exporter = new Exporter($config);
$exporter->export('users_converted.csv', $collection);
```

下記のCSVが出力されます。

```users_converted.csv
1,TOM,tomcruiseikemen@example.com
2,TARO,taro@example.com
3,HANAKO,hanako@eample.com
```

#まとめ
使い方が簡単。
行数がかなり多いCSVを扱う時にメモリの効率化が可能。

実際にプロジェクトで使用したりしています。

メモリ使用量の比較などはあとで時間があったらやってみます。