👨‍💼

#35 PHP CSVを読み込み、文字化けさせずに配列で出力する

2024/08/21に公開

はじめに

今回はPHPでCSVを読み込み、配列として一行ずつ出力する方法について紹介します。
データのやり取りによく使われるCSVについてや、使用する関数について簡単に解説していますので、最後までご覧いただけましたら幸いです。

CSVを用意する

CSVとは

Comma Separated Valueの略で、カンマでフィールドを区切ったテキスト形式のファイルです。
ソフトウェア同士での互換性が高く、手元でのデータの修正ができたりのとデータの管理しやすくなります。

sample.csv

商品名,値段
チョコレート,"100"
クッキー,"200"
ケーキ,"3,000"

先頭行が表の見出し、2行目以降がデータとなります。

ダブルクオーテーションの意味

ダブルクオーテーション(")で囲まれた文字列は、区切り文字があっても区切らず、一つの値とみなします。
例えば『10,000』という値があるときそれぞれ以下のようになります。

『10,000』 → 『10』『000』
『"10,000"』 → 『10,000』

文字コードについて

fgetcsv()とロケール

今回使用するfgetcsv()はロケールの設定に依存します。
そのためロケールとCSVの文字コードが異なると、以下のように区切り文字を正しく認識できないことがあります。

『"おこづかい","10,000"』
→ 『おこづかい",10』 『000』

5C問題

また、文字コードがShift-JISのCSVを直接fgetcsv()で読み込むと、
文字の2バイト目が「5C」になる文字がエスケープ文字「\」と判断され、CSVの形式が崩れてしまうことがあります。

これらの問題を避けるため、ロケールとCSVの文字コードを変換してからファイルを読み込ませます。

PHPでCSVを読み込み、配列を出力する

CSVを用意出来たら、早速読み込んでみましょう。

コード

<?php
// phpプログラムが扱うロケールを「ja_JP.UTF-8」に設定します。
setlocale(LC_ALL, ".UTF8");

// sample.csvの中身を取得し文字列に変換します。
$file = file_get_contents('sample.csv');

// $fileの文字コードをUTF-8に変換し$utf8_dataに格納します。
$utf8_data = mb_convert_encoding($file, 'UTF-8');

// テンポラリファイル$tmpを作成します。
$tmp = tmpfile();

// $utf8_dataの中身を$tmpに書き込みます。
fwrite($tmp, $utf8_data);

// $tmpのファイルポインタの位置を先頭に戻します。
rewind($tmp);

// $tmpを一行ずつ読み込み、出力します。
while ($data = fgetcsv($tmp)) {
    print_r($data);
}

// $tmpを削除します。
fclose($tmp);
?>

解説

setlocale(ロケール設定により影響を受ける関数のカテゴリ, "ロケール名");

ロケール情報を設定する
カテゴリに LC_ALL を指定すると全てのロケールの環境設定を一括で上書きできる

file_get_contents('ファイル名')

ファイルを読み込み、中身のデータを文字列として返す

mb_convert_encoding('ファイル名','変換したい文字コード')

ファイルを今の文字コードから別の文字コードに変換する

tmpfile()

読み取り/書き込み用のテンポラリファイルを作成し、ファイルポインタを返す
ファイルを fopen() したときと同様のファイルポインタが返される

fwrite(ファイルポインタ, 文字列やデータ);

文字列やデータをファイルに書き込む

rewind(ファイルポインタ)

ファイルポインタの位置を先頭に戻す

fgetcsv(ファイルポインタ)

ファイルポインタの位置からデータを一行分読み込み、添字配列にして返す
「"」を外し、区切り文字(指定がなければ「,」)でフィールドごとにデータを分ける
処理の後、ファイルポインタは次の行に移動する
ファイルの終端に達するとfalseを返す

while(条件式){ 繰り返したい処理 }

条件式がtrueを返す間処理を繰り返し、falseになったときループを抜ける

print_r(変数)

指定した変数に関する情報を解りやすく出力する

fclose(ファイルポインタ)

オープンされたファイルポインタをクローズする
※テンポラリファイルの場合、fcloseを呼ぶと自動的に削除される

出力結果

上記のコードを実行すると以下のように出力されます。

Array
(
    [0] => 商品名
    [1] => 値段
)
Array
(
    [0] => チョコレート
    [1] => 100
)
Array
(
    [0] => クッキー
    [1] => 200
)
Array
(
    [0] => ケーキ
    [1] => 3,000

)

CSVの中身を一行ずつ配列として出力することができました。

余談

今回は、テンポラリファイル$tmpを作成しfgetcsv()を使ってデータを読み込みましたが、
str_getcsv()を使うとCSV形式の文字列を配列として読み込むことができるので今後紹介できたらと思います。

最後に

今回は、PHPでCSVのデータを配列として出力する方法についてご紹介しました。
応用するとDBとのやり取りなど便利に使えるので、参考になれば幸いです。
ご覧いただきありがとうございました。

出典:
https://www.php.net/manual/ja/book.filesystem.php
https://qiita.com/Ken_tk/items/5f3715d7f97c620fa42c

Discussion