これはメモです

それ以上でもそれ以下でもないのです

PHP CSVの1行目をキー、2行目以降を値にした連想配列を作りたい

例えば以下のようなCSVデータがあって、

"name","email"
"Sato Asuka","satoasuka@example.com"
"Goto Yousuke","yousukegoto@example.net"
"Fujita Takahiro","takahiro_fujita@example.org"

下記のような形にして扱えるようにしたいということです。

<?php
[
    [
        'name' => 'Sato Asuka',
        'email' => 'satoasuka@example.com',
    ],
    [
        'name' => 'Goto Yousuke',
        'email' => 'yousukegoto@example.net',
    ],
    [
        'name' => 'Fujita Takahiro',
        'email' => 'takahiro_fujita@example.org',
    ],
];

1. CSVファイルの読み取り

ファイル読み取りは内部関数・ファイル操作クラスを使った方法がありますが、 CSVの場合は後者のほうがスムーズなようです。

<?php
$file = new SplFileObject('./dummy.csv');
// CSVを読み取るためのオプションを指定
$file->setFlags(
    SplFileObject::READ_CSV // CSV形式のファイルを読み込む
        | SplFileObject::DROP_NEW_LINE // 行末の改行を削除
        | SplFileObject::READ_AHEAD // 先読み・巻き戻しで読み出す(?)
        | SplFileObject::SKIP_EMPTY // 空行をスキップする。 READ_AHEADの指定がないと無効
);

READ_AHEADオプションについてはよくわかりませんでした…。(どこを見ても「先読み・巻き戻し」としか書いてない)

SplFileObjectクラスはIteratorインターフェースを実装しているため、 foreachでループさせることができます。

<?php
// 1行ずつループ処理
foreach ($file as $line) {
    mb_convert_variables('UTF-8', 'SJIS-win', $line); // 任意:CSVはSJISで保存されていることが多く、その場合UTF-8への変換が必要
    print_r($line);
}

■結果

Array
(
    [0] => name
    [1] => email
)
Array
(
    [0] => Sato Asuka
    [1] => satoasuka@example.com
)
Array
(
    [0] => Goto Yousuke
    [1] => yousukegoto@example.net
)
Array
(
    [0] => Fujita Takahiro
    [1] => takahiro_fujita@example.org
)

とりあえず配列として取り出すことができました。

2. キーの配列と値の配列に分ける

ループの1番目がキーにしたい行なので、インデックスを指定すれば分けられそうです。

<?php
$keys = [];
$userList = [];
foreach ($file as $i => $line) {
    mb_convert_variables('UTF-8', 'SJIS-win', $line);
    if ($i === 0) {
        $keys = $line;
    } else {
        $userList[] = $line;
    }
}
print_r($keys);
print_r($userList);

■結果

Array
(
    [0] => name
    [1] => email
)
Array
(
    [0] => Array
        (
            [0] => Sato Asuka
            [1] => satoasuka@example.com
        )

    [1] => Array
        (
            [0] => Goto Yousuke
            [1] => yousukegoto@example.net
        )

    [2] => Array
        (
            [0] => Fujita Takahiro
            [1] => takahiro_fujita@example.org
        )

)

問題なく分けられました。

3. 2.で得られた配列から、表題の連想配列を作成する

「2つの配列をキー、値にして一つにする関数とかありそうだな。PHP死ぬほど関数あるし」と思ってたらありました。

www.php.net

キーと値の要素数が一致しないと配列が作成できないため、ループ内で配列を作成します。

<?php
$keys = [];
$userList = [];
foreach ($file as $i => $line) {
    mb_convert_variables('UTF-8', 'SJIS-win', $line);
    if ($i === 0) {
        $keys = $line;
    } else {
-        $userList[] = $line;
+        $userList[] = array_combine($keys, $line);
    }
}
- print_r($keys);
print_r($userList);

■結果

Array
(
    [0] => Array
        (
            [name] => Sato Asuka
            [email] => satoasuka@example.com
        )

    [1] => Array
        (
            [name] => Goto Yousuke
            [email] => yousukegoto@example.net
        )

    [2] => Array
        (
            [name] => Fujita Takahiro
            [email] => takahiro_fujita@example.org
        )

)