こんにちは!今回は、PHPの標準関数であるstr_getcsv()について詳しく解説していきます。CSV形式の文字列を簡単に配列に変換できる、非常に便利な関数です!
str_getcsv関数とは?
str_getcsv()関数は、CSV形式の文字列を解析して配列に変換する関数です。
“string get CSV”の略で、CSVデータの1行を解析してフィールドごとに分割します。ダブルクォートのエスケープ処理やカスタム区切り文字にも対応しています!
基本的な構文
str_getcsv(
string $string,
string $separator = ',',
string $enclosure = '"',
string $escape = '\\'
): array
- $string: 解析するCSV文字列
- $separator: フィールド区切り文字(デフォルト: カンマ)
- $enclosure: フィールドを囲む文字(デフォルト: ダブルクォート)
- $escape: エスケープ文字(デフォルト: バックスラッシュ)
- 戻り値: 解析された配列
基本的な使用例
シンプルなCSV解析
// 基本的な使用
$csv = "apple,banana,orange";
$data = str_getcsv($csv);
print_r($data);
/*
Array (
[0] => apple
[1] => banana
[2] => orange
)
*/
// スペースを含むデータ
$csv = "John Doe,30,New York";
$data = str_getcsv($csv);
print_r($data);
/*
Array (
[0] => John Doe
[1] => 30
[2] => New York
)
*/
ダブルクォートで囲まれたフィールド
// クォートで囲まれたフィールド
$csv = '"Smith, John","Engineer","New York, NY"';
$data = str_getcsv($csv);
print_r($data);
/*
Array (
[0] => Smith, John
[1] => Engineer
[2] => New York, NY
)
*/
// クォート内のクォート
$csv = '"He said ""Hello""","World"';
$data = str_getcsv($csv);
print_r($data);
/*
Array (
[0] => He said "Hello"
[1] => World
)
*/
カスタム区切り文字
// タブ区切り(TSV)
$tsv = "apple\tbanana\torange";
$data = str_getcsv($tsv, "\t");
print_r($data);
/*
Array (
[0] => apple
[1] => banana
[2] => orange
)
*/
// セミコロン区切り
$csv = "apple;banana;orange";
$data = str_getcsv($csv, ';');
print_r($data);
/*
Array (
[0] => apple
[1] => banana
[2] => orange
)
*/
// パイプ区切り
$csv = "apple|banana|orange";
$data = str_getcsv($csv, '|');
print_r($data);
/*
Array (
[0] => apple
[1] => banana
[2] => orange
)
*/
空のフィールド
// 空のフィールド
$csv = "apple,,orange";
$data = str_getcsv($csv);
print_r($data);
/*
Array (
[0] => apple
[1] =>
[2] => orange
)
*/
// 先頭と末尾が空
$csv = ",banana,";
$data = str_getcsv($csv);
print_r($data);
/*
Array (
[0] =>
[1] => banana
[2] =>
)
*/
実践的な使用例
例1: CSVパーサー
class CsvParser {
private $separator;
private $enclosure;
private $escape;
public function __construct($separator = ',', $enclosure = '"', $escape = '\\') {
$this->separator = $separator;
$this->enclosure = $enclosure;
$this->escape = $escape;
}
/**
* CSV文字列を解析
*/
public function parse($csvString) {
return str_getcsv(
$csvString,
$this->separator,
$this->enclosure,
$this->escape
);
}
/**
* 複数行のCSVを解析
*/
public function parseMultiLine($csvText) {
$lines = explode("\n", trim($csvText));
$data = [];
foreach ($lines as $line) {
if (!empty(trim($line))) {
$data[] = $this->parse($line);
}
}
return $data;
}
/**
* ヘッダー付きCSVを解析(連想配列に変換)
*/
public function parseWithHeaders($csvText) {
$lines = explode("\n", trim($csvText));
if (empty($lines)) {
return [];
}
// 最初の行をヘッダーとして取得
$headers = $this->parse(array_shift($lines));
$data = [];
foreach ($lines as $line) {
if (empty(trim($line))) {
continue;
}
$values = $this->parse($line);
$row = [];
foreach ($headers as $i => $header) {
$row[$header] = $values[$i] ?? null;
}
$data[] = $row;
}
return $data;
}
/**
* CSVファイルを読み込んで解析
*/
public function parseFile($filename) {
if (!file_exists($filename)) {
return [];
}
$content = file_get_contents($filename);
return $this->parseMultiLine($content);
}
/**
* 特定の列のみを抽出
*/
public function extractColumns($csvText, $columnIndexes) {
$data = $this->parseMultiLine($csvText);
$extracted = [];
foreach ($data as $row) {
$extractedRow = [];
foreach ($columnIndexes as $index) {
$extractedRow[] = $row[$index] ?? null;
}
$extracted[] = $extractedRow;
}
return $extracted;
}
}
// 使用例
echo "=== CSVパーサー ===\n";
$parser = new CsvParser();
// 単一行の解析
$csv = '"Smith, John",30,"New York, NY"';
$data = $parser->parse($csv);
print_r($data);
// 複数行の解析
$csvText = <<<CSV
apple,100,red
banana,150,yellow
orange,120,orange
CSV;
echo "\n=== 複数行解析 ===\n";
$data = $parser->parseMultiLine($csvText);
print_r($data);
// ヘッダー付き
$csvWithHeaders = <<<CSV
name,age,city
John,30,New York
Alice,25,Tokyo
Bob,35,London
CSV;
echo "\n=== ヘッダー付き解析 ===\n";
$data = $parser->parseWithHeaders($csvWithHeaders);
print_r($data);
// 特定列の抽出
echo "\n=== 列抽出(0列目と2列目) ===\n";
$extracted = $parser->extractColumns($csvText, [0, 2]);
print_r($extracted);
例2: データインポーター
class DataImporter {
/**
* CSVデータをインポート
*/
public static function importCsv($csvText, $mapping = null) {
$lines = explode("\n", trim($csvText));
$imported = [];
foreach ($lines as $line) {
if (empty(trim($line))) {
continue;
}
$data = str_getcsv($line);
// マッピングがある場合は適用
if ($mapping !== null) {
$mappedData = [];
foreach ($mapping as $index => $key) {
$mappedData[$key] = $data[$index] ?? null;
}
$imported[] = $mappedData;
} else {
$imported[] = $data;
}
}
return $imported;
}
/**
* CSVからデータベースレコードを作成
*/
public static function createRecords($csvText, $table, $columns) {
$data = self::importCsv($csvText);
$records = [];
foreach ($data as $row) {
$record = [];
foreach ($columns as $i => $column) {
$record[$column] = $row[$i] ?? null;
}
$records[] = $record;
}
return [
'table' => $table,
'records' => $records
];
}
/**
* 型変換を適用
*/
public static function importWithTypes($csvText, $types) {
$data = self::importCsv($csvText);
$converted = [];
foreach ($data as $row) {
$convertedRow = [];
foreach ($row as $i => $value) {
$type = $types[$i] ?? 'string';
$convertedRow[] = self::convertType($value, $type);
}
$converted[] = $convertedRow;
}
return $converted;
}
/**
* 型変換
*/
private static function convertType($value, $type) {
switch ($type) {
case 'int':
return (int)$value;
case 'float':
return (float)$value;
case 'bool':
return filter_var($value, FILTER_VALIDATE_BOOLEAN);
case 'date':
return date('Y-m-d', strtotime($value));
default:
return $value;
}
}
/**
* バリデーション付きインポート
*/
public static function importWithValidation($csvText, $rules) {
$data = self::importCsv($csvText);
$validated = [];
$errors = [];
foreach ($data as $rowIndex => $row) {
$isValid = true;
$rowErrors = [];
foreach ($rules as $columnIndex => $rule) {
$value = $row[$columnIndex] ?? null;
if (!self::validateField($value, $rule)) {
$isValid = false;
$rowErrors[$columnIndex] = "Validation failed for column {$columnIndex}";
}
}
if ($isValid) {
$validated[] = $row;
} else {
$errors[$rowIndex] = $rowErrors;
}
}
return [
'valid' => $validated,
'errors' => $errors
];
}
/**
* フィールドのバリデーション
*/
private static function validateField($value, $rule) {
switch ($rule) {
case 'required':
return !empty($value);
case 'numeric':
return is_numeric($value);
case 'email':
return filter_var($value, FILTER_VALIDATE_EMAIL) !== false;
default:
return true;
}
}
}
// 使用例
echo "=== データインポート ===\n";
$csvText = <<<CSV
John,30,john@example.com
Alice,25,alice@example.com
Bob,35,bob@example.com
CSV;
// マッピング付きインポート
$mapping = ['name', 'age', 'email'];
$data = DataImporter::importCsv($csvText, $mapping);
print_r($data);
// 型変換付きインポート
echo "\n=== 型変換 ===\n";
$csvNumbers = <<<CSV
100,99.99,true
200,149.50,false
CSV;
$types = ['int', 'float', 'bool'];
$converted = DataImporter::importWithTypes($csvNumbers, $types);
print_r($converted);
// バリデーション付きインポート
echo "\n=== バリデーション ===\n";
$csvToValidate = <<<CSV
John,30,john@example.com
,25,alice@example.com
Bob,abc,invalid-email
CSV;
$rules = [0 => 'required', 1 => 'numeric', 2 => 'email'];
$result = DataImporter::importWithValidation($csvToValidate, $rules);
echo "有効なレコード: " . count($result['valid']) . "件\n";
echo "エラー: " . count($result['errors']) . "件\n";
print_r($result['errors']);
例3: CSVフィルター
class CsvFilter {
/**
* 条件に一致する行をフィルタリング
*/
public static function filter($csvText, $columnIndex, $value) {
$lines = explode("\n", trim($csvText));
$filtered = [];
foreach ($lines as $line) {
if (empty(trim($line))) {
continue;
}
$data = str_getcsv($line);
if (isset($data[$columnIndex]) && $data[$columnIndex] === $value) {
$filtered[] = $data;
}
}
return $filtered;
}
/**
* 複数条件でフィルタリング
*/
public static function filterMultiple($csvText, $conditions) {
$lines = explode("\n", trim($csvText));
$filtered = [];
foreach ($lines as $line) {
if (empty(trim($line))) {
continue;
}
$data = str_getcsv($line);
$matches = true;
foreach ($conditions as $columnIndex => $value) {
if (!isset($data[$columnIndex]) || $data[$columnIndex] !== $value) {
$matches = false;
break;
}
}
if ($matches) {
$filtered[] = $data;
}
}
return $filtered;
}
/**
* 範囲でフィルタリング
*/
public static function filterRange($csvText, $columnIndex, $min, $max) {
$lines = explode("\n", trim($csvText));
$filtered = [];
foreach ($lines as $line) {
if (empty(trim($line))) {
continue;
}
$data = str_getcsv($line);
if (isset($data[$columnIndex])) {
$value = (float)$data[$columnIndex];
if ($value >= $min && $value <= $max) {
$filtered[] = $data;
}
}
}
return $filtered;
}
/**
* パターンマッチでフィルタリング
*/
public static function filterPattern($csvText, $columnIndex, $pattern) {
$lines = explode("\n", trim($csvText));
$filtered = [];
foreach ($lines as $line) {
if (empty(trim($line))) {
continue;
}
$data = str_getcsv($line);
if (isset($data[$columnIndex]) &&
preg_match($pattern, $data[$columnIndex])) {
$filtered[] = $data;
}
}
return $filtered;
}
/**
* 重複を除去
*/
public static function removeDuplicates($csvText, $keyColumn = null) {
$lines = explode("\n", trim($csvText));
$seen = [];
$unique = [];
foreach ($lines as $line) {
if (empty(trim($line))) {
continue;
}
$data = str_getcsv($line);
if ($keyColumn !== null) {
$key = $data[$keyColumn] ?? null;
if (!isset($seen[$key])) {
$seen[$key] = true;
$unique[] = $data;
}
} else {
$key = implode('|', $data);
if (!isset($seen[$key])) {
$seen[$key] = true;
$unique[] = $data;
}
}
}
return $unique;
}
}
// 使用例
echo "=== CSVフィルター ===\n";
$csvText = <<<CSV
apple,100,red
banana,150,yellow
orange,120,orange
apple,90,green
grape,200,purple
CSV;
// 単一条件フィルタ
echo "名前が'apple'の行:\n";
$filtered = CsvFilter::filter($csvText, 0, 'apple');
print_r($filtered);
// 範囲フィルタ
echo "\n価格が100-150の行:\n";
$filtered = CsvFilter::filterRange($csvText, 1, 100, 150);
print_r($filtered);
// パターンマッチ
echo "\n色が'e'で終わる行:\n";
$filtered = CsvFilter::filterPattern($csvText, 2, '/e$/');
print_r($filtered);
// 重複除去
echo "\n重複除去(0列目をキーに):\n";
$unique = CsvFilter::removeDuplicates($csvText, 0);
print_r($unique);
例4: CSVトランスフォーマー
class CsvTransformer {
/**
* 列を追加
*/
public static function addColumn($csvText, $value, $position = -1) {
$lines = explode("\n", trim($csvText));
$transformed = [];
foreach ($lines as $line) {
if (empty(trim($line))) {
continue;
}
$data = str_getcsv($line);
if ($position === -1) {
$data[] = is_callable($value) ? $value($data) : $value;
} else {
array_splice($data, $position, 0,
[is_callable($value) ? $value($data) : $value]);
}
$transformed[] = $data;
}
return $transformed;
}
/**
* 列を削除
*/
public static function removeColumn($csvText, $columnIndex) {
$lines = explode("\n", trim($csvText));
$transformed = [];
foreach ($lines as $line) {
if (empty(trim($line))) {
continue;
}
$data = str_getcsv($line);
unset($data[$columnIndex]);
$transformed[] = array_values($data);
}
return $transformed;
}
/**
* 列の順序を変更
*/
public static function reorderColumns($csvText, $order) {
$lines = explode("\n", trim($csvText));
$transformed = [];
foreach ($lines as $line) {
if (empty(trim($line))) {
continue;
}
$data = str_getcsv($line);
$reordered = [];
foreach ($order as $index) {
$reordered[] = $data[$index] ?? null;
}
$transformed[] = $reordered;
}
return $transformed;
}
/**
* 値を変換
*/
public static function transformValues($csvText, $columnIndex, $callback) {
$lines = explode("\n", trim($csvText));
$transformed = [];
foreach ($lines as $line) {
if (empty(trim($line))) {
continue;
}
$data = str_getcsv($line);
if (isset($data[$columnIndex])) {
$data[$columnIndex] = $callback($data[$columnIndex]);
}
$transformed[] = $data;
}
return $transformed;
}
/**
* 行をマージ
*/
public static function mergeRows($csvText1, $csvText2) {
$lines1 = explode("\n", trim($csvText1));
$lines2 = explode("\n", trim($csvText2));
$merged = [];
$maxLines = max(count($lines1), count($lines2));
for ($i = 0; $i < $maxLines; $i++) {
$data1 = isset($lines1[$i]) && !empty(trim($lines1[$i])) ?
str_getcsv($lines1[$i]) : [];
$data2 = isset($lines2[$i]) && !empty(trim($lines2[$i])) ?
str_getcsv($lines2[$i]) : [];
$merged[] = array_merge($data1, $data2);
}
return $merged;
}
}
// 使用例
echo "=== CSVトランスフォーマー ===\n";
$csvText = <<<CSV
apple,100
banana,150
orange,120
CSV;
// 列を追加(固定値)
echo "列追加(固定値):\n";
$transformed = CsvTransformer::addColumn($csvText, 'fruit');
print_r($transformed);
// 列を追加(計算値)
echo "\n列追加(計算値 - 価格×1.1):\n";
$transformed = CsvTransformer::addColumn($csvText, function($row) {
return isset($row[1]) ? $row[1] * 1.1 : 0;
});
print_r($transformed);
// 列を削除
echo "\n列削除(1列目):\n";
$transformed = CsvTransformer::removeColumn($csvText, 1);
print_r($transformed);
// 列の順序を変更
echo "\n列の順序変更(逆順):\n";
$transformed = CsvTransformer::reorderColumns($csvText, [1, 0]);
print_r($transformed);
// 値を変換(大文字に)
echo "\n値を変換(0列目を大文字に):\n";
$transformed = CsvTransformer::transformValues($csvText, 0, 'strtoupper');
print_r($transformed);
例5: CSVバリデーター
class CsvValidator {
/**
* 列数をチェック
*/
public static function validateColumnCount($csvText, $expectedCount) {
$lines = explode("\n", trim($csvText));
$errors = [];
foreach ($lines as $lineNumber => $line) {
if (empty(trim($line))) {
continue;
}
$data = str_getcsv($line);
if (count($data) !== $expectedCount) {
$errors[] = [
'line' => $lineNumber + 1,
'expected' => $expectedCount,
'actual' => count($data),
'data' => $data
];
}
}
return [
'valid' => empty($errors),
'errors' => $errors
];
}
/**
* データ型をチェック
*/
public static function validateDataTypes($csvText, $types) {
$lines = explode("\n", trim($csvText));
$errors = [];
foreach ($lines as $lineNumber => $line) {
if (empty(trim($line))) {
continue;
}
$data = str_getcsv($line);
foreach ($types as $columnIndex => $type) {
if (!isset($data[$columnIndex])) {
continue;
}
$value = $data[$columnIndex];
$isValid = false;
switch ($type) {
case 'int':
$isValid = filter_var($value, FILTER_VALIDATE_INT) !== false;
break;
case 'float':
$isValid = is_numeric($value);
break;
case 'email':
$isValid = filter_var($value, FILTER_VALIDATE_EMAIL) !== false;
break;
case 'date':
$isValid = strtotime($value) !== false;
break;
default:
$isValid = true;
}
if (!$isValid) {
$errors[] = [
'line' => $lineNumber + 1,
'column' => $columnIndex,
'value' => $value,
'expected_type' => $type
];
}
}
}
return [
'valid' => empty($errors),
'errors' => $errors
];
}
/**
* 必須フィールドをチェック
*/
public static function validateRequired($csvText, $requiredColumns) {
$lines = explode("\n", trim($csvText));
$errors = [];
foreach ($lines as $lineNumber => $line) {
if (empty(trim($line))) {
continue;
}
$data = str_getcsv($line);
foreach ($requiredColumns as $columnIndex) {
if (!isset($data[$columnIndex]) || trim($data[$columnIndex]) === '') {
$errors[] = [
'line' => $lineNumber + 1,
'column' => $columnIndex,
'message' => 'Required field is empty'
];
}
}
}
return [
'valid' => empty($errors),
'errors' => $errors
];
}
/**
* 一意性をチェック
*/
public static function validateUnique($csvText, $columnIndex) {
$lines = explode("\n", trim($csvText));
$seen = [];
$errors = [];
foreach ($lines as $lineNumber => $line) {
if (empty(trim($line))) {
continue;
}
$data = str_getcsv($line);
if (isset($data[$columnIndex])) {
$value = $data[$columnIndex];
if (isset($seen[$value])) {
$errors[] = [
'line' => $lineNumber + 1,
'column' => $columnIndex,
'value' => $value,
'duplicate_of_line' => $seen[$value]
];
} else {
$seen[$value] = $lineNumber + 1;
}
}
}
return [
'valid' => empty($errors),
'errors' => $errors
];
}
}
// 使用例
echo "=== CSVバリデーション ===\n";
// 列数チェック
$csvText = <<<CSV
apple,100,red
banana,150
orange,120,orange
CSV;
echo "列数チェック(期待値: 3列):\n";
$result = CsvValidator::validateColumnCount($csvText, 3);
echo "結果: " . ($result['valid'] ? 'OK' : 'NG') . "\n";
if (!$result['valid']) {
print_r($result['errors']);
}
// データ型チェック
$csvWithTypes = <<<CSV
John,30,john@example.com
Alice,25,alice@example.com
Bob,abc,invalid-email
CSV;
echo "\n=== データ型チェック ===\n";
$types = [1 => 'int', 2 => 'email'];
$result = CsvValidator::validateDataTypes($csvWithTypes, $types);
echo "結果: " . ($result['valid'] ? 'OK' : 'NG') . "\n";
if (!$result['valid']) {
print_r($result['errors']);
}
// 必須フィールドチェック
$csvWithEmpty = <<<CSV
John,30,New York
,25,Tokyo
Bob,,London
CSV;
echo "\n=== 必須フィールドチェック ===\n";
$result = CsvValidator::validateRequired($csvWithEmpty, [0, 1]);
echo "結果: " . ($result['valid'] ? 'OK' : 'NG') . "\n";
if (!$result['valid']) {
print_r($result['errors']);
}
// 一意性チェック
$csvWithDuplicates = <<<CSV
apple,100
banana,150
apple,90
CSV;
echo "\n=== 一意性チェック ===\n";
$result = CsvValidator::validateUnique($csvWithDuplicates, 0);
echo "結果: " . ($result['valid'] ? 'OK' : 'NG') . "\n";
if (!$result['valid']) {
print_r($result['errors']);
}
fgetcsv()との比較
// str_getcsv(): 文字列から直接解析
$csv = "apple,banana,orange";
$data = str_getcsv($csv);
print_r($data);
// fgetcsv(): ファイルから1行ずつ読み込み
$fp = fopen('data.csv', 'r');
while (($data = fgetcsv($fp)) !== false) {
print_r($data);
}
fclose($fp);
// 使い分け:
// - すでに文字列がある → str_getcsv()
// - ファイルから読み込む → fgetcsv()
まとめ
str_getcsv()関数の特徴をまとめると:
できること:
- CSV文字列を配列に変換
- カスタム区切り文字に対応
- クォートのエスケープ処理
- TSVやその他の区切り形式にも対応
推奨される使用場面:
- CSVデータの解析
- データインポート
- CSV文字列の処理
- APIレスポンスの解析
利点:
- シンプルで使いやすい
- クォート処理が自動
- カスタマイズ可能
注意点:
- 1行ずつしか処理できない
- 複数行は
explode()などと組み合わせる - ヘッダー処理は別途必要
関連関数:
fgetcsv(): ファイルからCSVを読み込みstr_putcsv(): 配列からCSV文字列を生成(存在しない、自作が必要)explode(): 文字列を分割
よく使うパターン:
// 基本的な解析
$data = str_getcsv($csv);
// TSV解析
$data = str_getcsv($tsv, "\t");
// セミコロン区切り
$data = str_getcsv($csv, ';');
// 複数行解析
$lines = explode("\n", $csvText);
foreach ($lines as $line) {
$data = str_getcsv($line);
}
str_getcsv()は、CSV形式のデータを簡単に配列に変換できる便利な関数です。データインポートやCSV解析で頻繁に使用されるので、しっかり使いこなしましょう!
