表計算ソフトやデータベースなどのデータの受け渡しに使うフォーマットとして、CSVというフォーマットがあります。このフォーマットはCSV(Comma-Separated Values)の名前のとおり、複数の値をカンマで区切っただけのテキストファイルで、書式さえ理解していれば特別なソフトを使わずに作成や修正をすることができるので便利です。今回はこのフォーマットの書式の説明と、PHPで読み込んで配列に格納または出力する関数の紹介をします。
CSVの書式
CSVでは値の区切りをカンマで、データ行の区切りを改行コードで行います。たとえば表計算ソフトで以下のような表があったとします。
世界ランキング | 名前 | コメント |
1 | みかん | 「次も頑張ります」 |
2 | りんご | 「妥当な結果です」 |
3 | メロン | 「スタートに失敗した。このまま腐ってしまっては仕方が無い」 |
この表をCSVファイルとして出力すると、次のようなテキストファイルが生成されるはずです。
世界ランキング,名前,コメント 1,みかん,「次も頑張ります」 2,りんご,「妥当な結果です」 3,メロン,「スタートに失敗した。このまま腐ってしまっては仕方が無い」
値の区切りがカンマで示され、行の区切りが改行で示されているのがわかります。値は”みかん”のように、ダブルクォーテーションで囲んでも構いません。ですが、注意すべきケースが3つ程存在します。
- 値がカンマを含んでいるケース
- 値が改行を含んでいるケース
- 値がダブルクォーテーションを含んでいるケース
先述の通りカンマは値同士の区切りとして特別な意味を持ちますので、値自体にカンマが含まれている場合、それが区切り文字でないことを明示しなければなりません。そこで先程値を囲むのに使ってもよいと書いたダブルクォーテーションで、カンマを含んだシーケンスを挟んでそれが一つの値であると明示するのに使います。たとえば「スタートに失敗した,このまま腐ってしまっては仕方が無い」という値を、次のように囲みます。
3,メロン,"「スタートに失敗した,このまま腐ってしまっては仕方が無い」"
値が改行を含んでいる場合にも、それが列の区切りではないと明示しなければなりません。この場合もダブルクォーテーションで囲むという方法が使えます。
「スタートに失敗した。
このまま腐ってしまっては仕方が無い」
という値の表記例を見てみましょう。
3,メロン,"「スタートに失敗した。 このまま腐ってしまっては仕方が無い」"
値にダブルクォーテーションが含まれている場合には、それが値の一部であって表記規則の一部でないことを明示しなければなりません。そのためにはまず値全体をダブルクォーテーションで括り、さらに値中のダブルクォーテーションは2つ重ねることによって間違って解釈されることを回避します。”スタートに失敗した。このまま腐ってしまっては仕方が無い”という値は、以下のように表記します。
3,メロン,"""スタートに失敗した。このまま腐ってしまっては仕方が無い"""
なぜダブルクォーテーションが3つ連続しているのかは、順序立てて考えると分かり易いです。大切な規則は、ダブルクォーテーションを使う場合かならず値の一番外側はダブルクォーテーションで囲まなければならないということです。
以上が基本的なCSVの書式になりますが、ソフトによって方言や独自の拡張文法も存在しています。が、国際標準として成文化されたルールは上記のものになりますので、データの広い受け渡しを想定するならばこのルールに倣っておきましょう。
PHPでのCSVファイルの取り扱い
PHPでは、CSVファイルを読み込んで二次元配列に格納する関数fgetcsvがヴァージョン4から、二次元配列をCSVのフォーマットに整形してファイルに書き込むfputcsvがヴァージョン5.1から存在しています。それぞれの書式は以下のようになります。
fgetcsv(引数1,引数2,引数3,引数4,引数5); /*ここからコメント 引数1はfopenなどで取得したファイルポインタ。 引数2以降は省略可能。 引数2 = 行の最大長 引数3 = 値の区切り文字(デフォルトはカンマ) 引数4 = 値の囲い文字(デフォルトはダブルクォーテーション) 引数5 = エスケープ文字(デフォルトはバックスラッシュ) */ fputcsv(引数1,引数2,引数3,引数4); /* 引数1 = ファイルポインタ 引数2 = 値の二次元配列 以下省略可 引数3 = 値の区切り文字(デフォルトはカンマ) 引数4 = 値の囲い文字(デフォルトはダブルクォーテーション) */ |
ただしfgetcsvの方は、値が日本語のときに上手く読み込めないという事例がネット上でも多数報告されています。どうもExcelなどのCSVの作成元が日本語文字コードにShift-JISを採用していて、PHPでユーザが設定している文字コードがEUC-JPまたはUTF-8という状況でのエラーが多いようです。さらに調べると、fgetcsv関数は文字コードの処理時に、default_charsetではなくPHPのロケール設定という利用者の地域設定を参照するようで、setlocaleという関数を使って地域と使用文字コードを明示的に設定してやらなければならないようです。
setlocale(LC_ALL, "ja_JP.UTF-8"); /* setlocale関数の書式 setlocale(引数1,引数2); 引数1 = ロケール設定が適用される関数のカテゴリ。 とりあえずLC_ALLを指定すると適用できる全ての関数に有効になる。 引数2 = ロケール名 ja_JPまでがロケール名だが、ドットの後ろに文字コードまで指定できる。 */ |
このsetlocale関数を用いて、Shift-JISのCSVファイルをUTF-8環境に読み込み表示するプログラムを個人的に実行してみました。が、結果として当方の環境ではやはり文字化けが起こってしまいました。したがって、当面の対策としては、エラーを避けるためにCSVファイルの方を整形してからプログラムに読み込ませるという方法を採用しています。具体的には、値をダブルクォーテーションで必ず囲むようにする、CSVファイルの文字コードをPHPでのエンコーディングに変換して保存し直すなどです。
CSVでのデータ受け渡しができることには計り知れないメリットがあるので、この問題が早々に解決してくれることを期待します。
ピンバック: fgetcsvで読み込んだCSVファイルをhtmlのテーブルとして出力 | AkisiのWEB制作日記