MUGIJIRU.JP

Webエンジニアの雑談ブログ

csvファイルの処理

csvファイルの処理はシステム開発ではポピュラーなものですが
Microsoft Excelなどで吐き出されるような

・セル内改行を含んでいたり
・ダブルクォートで値が囲まれていたり

といったデータの処理については、初見ではどうすればいいのか悩むものでした。

私はいつも使う関数を持っているのですが、これが随分と信頼性があり
業務システムにおいてこの関数を介して処理したデータでトラブルが起きたことがありません。

fgetcsv, splfileobject, explodeによるお手製処理など
選択肢はいろいろありますが、どれも何らかトラブルが起きることが多い中で
この関数は安定感があるようです。

貼っておきます。

<?php
/**
 * CSVファイルの1行を配列で取得
 * 複数行対応
 *
 * @param object $fh fopen()などで得られたファイルハンドラ
 * @param string $file_encode 処理するファイルの文字コード
 */
function ml_fgetcsv(&$fh, $file_encode = 'UTF-8')
{
    $values = array();

    if (feof($fh)) {
        return false;
    }

    while ($values === array()) {
        $csv = '';

        while (!feof($fh)) {
            $csv .= fgets($fh);

            if (((preg_match_all('/"/', $csv, $matches))%2) == 0) {
                break;
            }
        }

        if (mb_strlen(trim($csv)) === 0) {
            continue;
        }

        $temp = preg_replace('/(?:\x0D\x0A|[\x0D\x0A])?$/', ',', trim($csv), 1);

        preg_match_all('/("[^"]*(?:""[^"]*)*"|[^,]*),/', $temp, $matches);

        for ($i = 0 ; $i < count($matches[1]); $i ++) {
            if (preg_match('/^"(.*)"$/s', $matches[1][$i], $m)){
                $matches[1][$i] = preg_replace('/""/', '"', $m[1]);
            }

            if (strtoupper($file_encode) !== 'UTF-8') {
                $values[] = mb_convert_encoding($matches[1][$i], 'UTF-8', $file_encode);
            } else {
                $values[] = $matches[1][$i];
            }
        }
    }

    return $values;
}