[PHP]str_getcsv関数を完全解説!CSV文字列を配列に変換する方法

PHP

こんにちは!今回は、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解析で頻繁に使用されるので、しっかり使いこなしましょう!

タイトルとURLをコピーしました