PHP CSV 処理のスニペット集
このページは、PHP の CSV 処理のスニペットなどをまとめる予定のページです。
目次
注意
- コードのライセンスは CC0 (クレジット表示不要、改変可、商用可) です。
スニペット
CSV 読み込み (Shift_JIS)
$f = fopen('test.csv', 'r'); // データ取得
stream_filter_prepend($f, 'convert.iconv.cp932/utf-8'); // Shift_JIS のファイル読み込み用に変換フィルターをセット
$locale = setlocale(LC_CTYPE, 0);
setlocale(LC_CTYPE, 'C'); // fgetcsv() がロケールの影響を受けないよう C ロケールに変更
while (($row = fgetcsv($f)) !== false) { // CSV読み込み
print_r($row); // TODO ここで必要な処理を行います。
}
setlocale(LC_CTYPE, $locale); // ロケールを戻す
fclose($f);
CSV 書き込み (Shift_JIS)
$data = array(
array( 'id' => 1, 'name' => '山田', 'title' => '山田記事1'),
array( 'id' => 2, 'name' => '鈴木', 'title' => '鈴木記事2'),
array( 'id' => 3, 'name' => '東京', 'title' => '東京記事3'),
);
$f = fopen('test.csv', 'w');
stream_filter_prepend($f, 'convert.iconv.utf-8/cp932');
foreach ($data as $row) {
fputcsv($f, $row);
}
fclose($f);
CSV ダウンロード (Shift_JIS)
$data = array(
array( 'id' => 1, 'name' => '山田', 'title' => '山田記事1'),
array( 'id' => 2, 'name' => '鈴木', 'title' => '鈴木記事2'),
array( 'id' => 3, 'name' => '東京', 'title' => '東京記事3'),
);
$name = 'テスト.csv';
header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename*=UTF-8\'\'' . rawurlencode($name));
header('Cache-Control: no-store');
$f = fopen('php://output', 'w');
stream_filter_prepend($f, 'convert.iconv.utf-8/cp932');
foreach ($data as $row) {
fputcsv($f, $row);
}
fclose($f);
ユーティリティ関数
(主にフレームワークなどを使う必要が無い小さなプログラム用の関数です)
CSV 読み込み
Shift_JIS
/**
* Shift_JIS の CSV を読み込みます。(読み込んだデータは UTF-8 として扱います)
* @param string $filename ファイルパス (fopen() で処理可能なパス)
* @return array|false 成功した場合 CSV の行配列、失敗した場合 false を返します。(E_WARNING なども発生します)
*/
function csv_read_sjis($filename) {
$data = array();
$f = fopen($filename, 'rb');
if ($f === false) return false;
stream_filter_prepend($f, 'convert.iconv.cp932/utf-8');
$locale = setlocale(LC_CTYPE, 0);
setlocale(LC_CTYPE, 'C'); // fgetcsv() がロケールの影響を受けないよう C ロケールに変更
while (($row = fgetcsv($f)) !== false) $data[] = $row;
setlocale(LC_CTYPE, $locale); // ロケールを戻す
fclose($f);
return $data;
}
UTF-8
/**
* UTF-8 の CSV を読み込みます。
* @param string $filename ファイルパス (fopen() で処理可能なパス)
* @return array|false 成功した場合 CSV の行配列、失敗した場合 false を返します。(E_WARNING なども発生します)
*/
function csv_read($filename) {
$data = array();
$f = fopen($filename, 'rb');
if ($f === false) return false;
$locale = setlocale(LC_CTYPE, 0);
setlocale(LC_CTYPE, 'C'); // fgetcsv() がロケールの影響を受けないよう C ロケールに変更
while (($row = fgetcsv($f)) !== false) $data[] = $row;
setlocale(LC_CTYPE, $locale); // ロケールを戻す
fclose($f);
return $data;
}
CSV 書き込み (一括)
/**
* 連想配列の配列を CSV 出力します。
*
* @param resource $filename ファイルパス (fopen() で処理可能なパス)
* @param array $data 連想配列の配列
* @param array $options 出力設定
* <pre>
* array columnDefs: (必須) 列情報
* string name: (任意) データ行の連想配列のキー
* string headerText: (任意) ヘッダ行に出力する見出しの名前
* string groupHeaderText: (任意) headerTextの上に出力する見出しの名前 (もし1つでもこの項目の設定があれば、見出し行を計2行にする)
* bool main: (任意) 主要列か (true/false。未指定時は false) 。主要列の場合、前の行の値が同じ値だったときに値を空欄にします。
* string delimiter: (任意) 値を区切る文字 (',' など。未指定時はfputcsv()の既定値)
* string enclosure: (任意) 値を囲む文字 ('"' など。未指定時はfputcsv()の既定値)
* string escapeChar: (任意) 特殊文字をエスケープする文字 ("\\" など。未指定時はfputcsv()の既定値)
* string newLine: (任意) 改行コード (\n または \r\n)
* string encoding: (任意) 出力する文字エンコーディング (cp932 など iconv の文字エンコーディング名)
* </pre>
* @return void
*/
function csv_write($filename, $data, $options = array()) {
$f = fopen($filename, 'wb');
if ($f === false) return false;
$columnDefs = isset($options['columnDefs']) ? $options['columnDefs'] : array();
$delimiter = isset($options['delimiter']) ? $options['delimiter'] : ',';
$enclosure = isset($options['enclosure']) ? $options['enclosure'] :'"';
$escapeChar = isset($options['escapeChar']) ? $options['escapeChar'] :"\\";
$newLine = isset($options['newLine']) ? $options['newLine'] : null;
if ($newLine == "\r\n") {
stream_filter_append($f, 'csv_write_crlf');
}
$encoding = isset($options['encoding']) ? $options['encoding'] : null;
if ($encoding) {
stream_filter_append($f, 'convert.iconv.utf-8/' . $encoding);
}
// グループヘッダ行
$groupHeaderRow = array();
$groupHeaderRowRequired = false; // グループヘッダ出力必要か
foreach ($columnDefs as $col) {
$text = isset($col['groupHeaderText']) ? $col['groupHeaderText'] : '';
if (!$groupHeaderRowRequired && strlen($text) > 0) $groupHeaderRowRequired = true;
$groupHeaderRow[] = $text;
}
if ($groupHeaderRowRequired) {
fputcsv($f, $groupHeaderRow, $delimiter, $enclosure, $escapeChar);
}
// ヘッダ行
$headerRow = array();
$headerRowRequired = false; // ヘッダ出力必要か
foreach ($columnDefs as $col) {
$text = isset($col['headerText']) ? $col['headerText'] : '';
if (!$headerRowRequired && strlen($text) > 0) $headerRowRequired = true;
$headerRow[] = $text;
}
if ($headerRowRequired) {
fputcsv($f, $headerRow, $delimiter, $enclosure, $escapeChar);
}
if ($columnDefs) {
$lastMain = array();
foreach ($data as $row) {
$outputRow = array();
foreach ($columnDefs as $index => $col) {
$name = $col['name'];
$v = isset($row[$name]) ? $row[$name] : '';
$main = isset($col['main']) && $col['main'];
// 主要行の場合、前の値と同じであれば空で出力
if ($main) {
$lastMainValue = isset($lastMain[$name]) ? $lastMain[$name] : '';
if ($v === $lastMainValue) {
$v = '';
}
else {
$lastMain[$name] = $v;
}
}
// 取得できた値を出力行配列に追加
if (count($outputRow) - 1 != $index) {
for ($i = count($outputRow); $i < $index; $i++) {
$outputRow[] = '';
}
}
$outputRow[] = $v;
}
fputcsv($f, $outputRow, $delimiter, $enclosure, $escapeChar);
}
}
else {
foreach ($data as $row) {
fputcsv($f, $row, $delimiter, $enclosure, $escapeChar);
}
}
fclose($f);
}
// https://stackoverflow.com/questions/12722894/how-can-i-change-the-line-endings-used-by-fputcsv
class crlf_filter extends php_user_filter
{
function filter($in, $out, &$consumed, $closing)
{
while ($bucket = stream_bucket_make_writeable($in)) {
// make sure the line endings aren't already CRLF
$bucket->data = preg_replace("/(?<!\r)\n/", "\r\n", $bucket->data);
$consumed += $bucket->datalen;
stream_bucket_append($out, $bucket);
}
return PSFS_PASS_ON;
}
}
stream_filter_register('csv_write_crlf', 'crlf_filter');
// UTF-8 + LF で下記を出力
//
// 1,山田,山田記事1
// 1,山田,山田記事2
// 1,山田,山田記事3
// 1,山田,山田記事4
// 1,山田,山田記事5
// 2,鈴木,鈴木記事1
// 2,鈴木,鈴木記事2
// 3,東京,東京記事1
// 3,東京,東京記事2
// 3,東京,東京記事3
csv_write('test1.csv', $data);
// UTF-8 + LF で下記を出力
//
// ID,名前,タイトル
// 1,山田,山田記事1
// 1,山田,山田記事2
// 1,山田,山田記事3
// 1,山田,山田記事4
// 1,山田,山田記事5
// 2,鈴木,鈴木記事1
// 2,鈴木,鈴木記事2
// 3,東京,東京記事1
// 3,東京,東京記事2
// 3,東京,東京記事3
csv_write('test2.csv', $data, array(
'columnDefs' => array(
array( 'name' => 'id', 'headerText' => 'ID' ),
array( 'name' => 'name', 'headerText' => '名前' ),
array( 'name' => 'title', 'headerText' => 'タイトル' ),
),
));
// Shift_JIS + CRLF で下記を出力
//
// ID,名前,タイトル
// 1,山田,山田記事1
// ,,山田記事2
// ,,山田記事3
// ,,山田記事4
// ,,山田記事5
// 2,鈴木,鈴木記事1
// ,,鈴木記事2
// 3,東京,東京記事1
// ,,東京記事2
// ,,東京記事3
csv_write('test3.csv', $data, array(
'columnDefs' => array(
array( 'name' => 'id', 'headerText' => 'ID', 'main' => true ),
array( 'name' => 'name', 'headerText' => '名前', 'main' => true ),
array( 'name' => 'title', 'headerText' => 'タイトル', ),
),
'encoding' => 'cp932',
'newLine' => "\r\n"
));
// UTF-8 + LF で下記を出力
//
// ユーザー,,投稿
// ID,名前,タイトル
// 1,山田,山田記事1
// 1,山田,山田記事2
// 1,山田,山田記事3
// 1,山田,山田記事4
// 1,山田,山田記事5
// 2,鈴木,鈴木記事1
// 2,鈴木,鈴木記事2
// 3,東京,東京記事1
// 3,東京,東京記事2
// 3,東京,東京記事3
csv_write('test4.csv', $data, array(
'columnDefs' => array(
array( 'name' => 'id', 'headerText' => 'ID', 'groupHeaderText' => 'ユーザー' ),
array( 'name' => 'name', 'headerText' => '名前' ),
array( 'name' => 'title', 'headerText' => 'タイトル', 'groupHeaderText' => '投稿' ),
),
));