【PHP8.x】fgetcsv関数の使い方

fgetcsv関数の使い方について、初心者にもわかりやすく解説します。

作成日: 更新日:

基本的な使い方

『fgetcsv関数は…を実行する関数です』

fgetcsv関数は、fopen関数などによって開かれたファイルポインタから、CSV(Comma-Separated Values)形式のデータを1行ずつ読み込み、それを配列として解析する関数です。この関数は、CSVファイルの内容をプログラムで効率的に処理する際に広く利用されます。第一引数には、対象となる有効なファイルポインタを指定します。関数が呼び出されるたびに、ファイルポインタは次の行へと自動的に進みます。オプションの引数として、フィールドの区切り文字(デリミタ、デフォルトはカンマ)、フィールドを囲む文字(エンクロージャ、デフォルトはダブルクォーテーション)、そして読み込む行の最大長を指定することが可能です。正常に1行を読み込めた場合、各フィールドの値を要素として持つ配列を返します。ファイルの終端に達した場合や、読み込みエラーが発生した場合は false を返します。そのため、while ループと組み合わせて、ファイルの内容がなくなるまで繰り返し処理を行うのが一般的な使い方です。なお、PHP 8.0.0以降では、空行を読み込んだ場合は [null] という単一の null 値を含む配列が返されます。

構文(syntax)

1fgetcsv(
2    resource $stream,
3    ?int $length = null,
4    string $separator = ",",
5    string $enclosure = "\"",
6    string $escape = "\\"
7): array|false

引数(parameters)

resource $stream, ?int $length = null, string $separator = ',', string $enclosure = '"', string $escape = '\'

  • resource $stream: 読み込むCSVデータが含まれるファイルリソースを指定します。
  • ?int $length = null: 1行あたりに読み込む最大バイト数を指定します。省略すると、最大行長に制限はありません。
  • string $separator = ',': CSVの各フィールドを区切る文字を指定します。デフォルトはカンマ(,)です。
  • string $enclosure = '"': CSVのフィールドを囲む文字を指定します。デフォルトはダブルクォーテーション(")です。
  • string $escape = '\': $enclosure文字をエスケープするために使用される文字を指定します。デフォルトはバックスラッシュ()です。

戻り値(return)

array|false

fgetcsv関数は、ファイルポインタから1行を読み込み、CSV形式のデータを配列として返します。ファイルの終端に達したり、エラーが発生した場合はfalseを返します。

サンプルコード

PHP fgetcsvのバグ回避 CSV読み込み

1<?php
2
3/**
4 * fgetcsv のバグを回避するためのサンプルコード
5 *
6 * fgetcsv は、特に長い行や不正なフォーマットの CSV ファイルを処理する際に、
7 * 予期せぬ動作をすることがあります。
8 * このサンプルコードでは、fgets と str_getcsv を組み合わせて、
9 * より堅牢な CSV ファイルの読み込みを試みます。
10 *
11 * 注意: この方法は必ずしも全ての問題を解決するわけではありません。
12 *      CSV ファイルの構造によっては、さらなる対策が必要になる場合があります。
13 */
14
15function readCSVRobustly(string $filename, string $separator = ',', string $enclosure = '"', string $escape = '\\'): array
16{
17    $rows = [];
18    $file = fopen($filename, 'r');
19
20    if ($file === false) {
21        throw new Exception("ファイルを開けませんでした: $filename");
22    }
23
24    while (!feof($file)) {
25        $line = fgets($file);
26
27        if ($line === false) {
28            break; // ファイルの終端またはエラー
29        }
30
31        // 空行をスキップ
32        $line = trim($line);
33        if ($line === '') {
34            continue;
35        }
36        
37        $data = str_getcsv($line, $separator, $enclosure, $escape);
38        $rows[] = $data;
39    }
40
41    fclose($file);
42    return $rows;
43}
44
45// 使用例:
46$filename = 'data.csv'; // CSV ファイル名
47$separator = ',';
48$enclosure = '"';
49$escape = '\\';
50
51// サンプルデータを作成 (data.csv)
52$csv_data = [
53    ['名前', '年齢', '職業'],
54    ['山田太郎', '30', 'エンジニア'],
55    ['田中花子', '25', 'デザイナー'],
56    ['佐藤健太', '40', 'マネージャー'],
57];
58
59$fp = fopen($filename, 'w');
60foreach ($csv_data as $fields) {
61    fputcsv($fp, $fields);
62}
63fclose($fp);
64
65try {
66    $data = readCSVRobustly($filename, $separator, $enclosure, $escape);
67
68    // データの表示
69    foreach ($data as $row) {
70        print_r($row);
71    }
72} catch (Exception $e) {
73    echo 'エラー: ' . $e->getMessage() . PHP_EOL;
74} finally {
75    // 作成したファイルを削除
76    unlink($filename);
77}
78?>

PHPのfgetcsv関数は、ファイルストリームから1行ずつCSV形式のデータを読み込み、配列として返します。引数には、読み込むファイルストリーム $stream、最大行の長さ $length(省略可能)、区切り文字 $separator(デフォルトはカンマ)、囲み文字 $enclosure(デフォルトはダブルクォート)、エスケープ文字 $escape(デフォルトはバックスラッシュ)を指定します。正常に読み込めた場合は配列を返し、ファイルの終端に達した場合やエラーが発生した場合は false を返します。

しかし、fgetcsv 関数は、非常に長い行や不正な形式のCSVファイルを扱う際に、予期せぬ動作をすることがあります。このサンプルコードは、fgets 関数と str_getcsv 関数を組み合わせることで、より堅牢にCSVファイルを読み込む方法を示しています。

readCSVRobustly 関数は、ファイルを開き、fgets 関数で1行ずつ読み込みます。読み込んだ行は str_getcsv 関数で解析され、配列として $rows に追加されます。空行はスキップされます。この方法により、fgetcsv 関数が抱える可能性のある問題を回避できます。ただし、この方法でも全ての問題を解決できるわけではありません。CSVファイルの構造によっては、さらなる対策が必要となる場合があります。

サンプルコードでは、data.csv という名前のサンプルCSVファイルを作成し、readCSVRobustly 関数を使用して読み込んでいます。読み込んだデータは print_r 関数で表示されます。エラーが発生した場合は、エラーメッセージが表示されます。最後に、作成したサンプルファイルを削除します。このサンプルは、システムエンジニアを目指す方が、CSVファイルを安全に読み込むためのヒントとなるでしょう。

fgetcsv関数は、CSVファイルを読み込む際に便利な関数ですが、いくつかの注意点があります。特に、長い行や不正な形式のCSVファイルを扱う際に、予期せぬ動作をする可能性があります。サンプルコードでは、fgets関数で1行ずつ読み込み、str_getcsv関数でCSV形式に分解することで、より堅牢な読み込みを試みています。ただし、この方法でも全てのケースに対応できるわけではありません。CSVファイルの構造によっては、特殊な文字のエスケープ処理や、文字コードの問題など、追加の対策が必要となる場合があります。readCSVRobustly関数の引数で区切り文字、囲み文字、エスケープ文字を指定できます。これらがCSVファイルと一致しているか確認してください。ファイルを開けなかった場合や、処理中にエラーが発生した場合の例外処理も重要です。

PHP fgetcsv 文字化け解消 CSV読み込み

1<?php
2
3/**
4 * fgetcsv で発生しやすい文字化け問題を解決しながらCSVファイルを読み込む関数。
5 *
6 * この関数は、Shift-JISなどのUTF-8以外のエンコーディングで保存されたCSVファイルを
7 * 読み込む際に発生する文字化けを `mb_convert_encoding` を使用して解決し、
8 * UTF-8エンコーディングのデータとして返します。
9 *
10 * @param string $filePath CSVファイルのパス。
11 * @param string $fromEncoding CSVファイルの元のエンコーディング (例: 'SJIS-win', 'EUC-JP')。
12 * @param string $toEncoding 変換したいエンコーディング (通常は 'UTF-8')。
13 * @param string $separator フィールドの区切り文字 (デフォルトはカンマ)。
14 * @param string $enclosure フィールドを囲む文字 (デフォルトはダブルクォーテーション)。
15 * @param string $escape エスケープ文字 (デフォルトはバックスラッシュ)。
16 * @return array|false 読み込んだデータの配列、またはエラー時に false を返します。
17 */
18function readCsvWithEncodingConversion(
19    string $filePath,
20    string $fromEncoding = 'SJIS-win',
21    string $toEncoding = 'UTF-8',
22    string $separator = ',',
23    string $enclosure = '"',
24    string $escape = '\\'
25): array|false {
26    // ファイルが存在しない場合はエラーを報告して終了
27    if (!file_exists($filePath)) {
28        echo "エラー: ファイル '{$filePath}' が見つかりません。\n";
29        return false;
30    }
31
32    // ファイルを読み込みモードで開きます
33    $handle = fopen($filePath, 'r');
34    if ($handle === false) {
35        echo "エラー: ファイル '{$filePath}' を開けませんでした。\n";
36        return false;
37    }
38
39    $data = [];
40    // CSVファイルを1行ずつ読み込みます
41    while (($rowData = fgetcsv($handle, 0, $separator, $enclosure, $escape)) !== false) {
42        // 各行の各要素を、指定された元のエンコーディングから目的のエンコーディングに変換します。
43        // fgetcsvはバイト列としてデータを読み込むため、この変換が文字化け防止に重要です。
44        $decodedRow = array_map(function ($value) use ($fromEncoding, $toEncoding) {
45            return mb_convert_encoding($value, $toEncoding, $fromEncoding);
46        }, $rowData);
47        $data[] = $decodedRow;
48    }
49
50    // ファイルを閉じます
51    fclose($handle);
52    return $data;
53}
54
55// --- 以下は `readCsvWithEncodingConversion` 関数の利用例です ---
56
57// 1. サンプルCSVファイルを一時的に作成します。
58//    このファイルは、Shift-JIS (Windows-31J / CP932) でエンコードされています。
59//    これにより、fgetcsvでそのまま読み込むと文字化けが発生する状況をシミュレートします。
60$tempFileName = sys_get_temp_dir() . '/sample_sjis_for_fgetcsv.csv';
61$csvContent = [
62    ['商品ID', '商品名', '価格'],
63    ['1', 'りんご', '100'],
64    ['2', 'みかん', '80'],
65    ['3', 'バナナ', '120'],
66];
67
68$fileHandle = fopen($tempFileName, 'w');
69if ($fileHandle === false) {
70    echo "エラー: 一時ファイル '{$tempFileName}' の作成に失敗しました。\n";
71    exit(1);
72}
73
74// CSVファイルに書き込む前に、UTF-8の文字列をShift-JIS-winに変換します。
75// これにより、読み込む際にShift-JISとして扱えるファイルが作成されます。
76foreach ($csvContent as $row) {
77    $convertedRowForWrite = array_map(function ($value) {
78        return mb_convert_encoding($value, 'SJIS-win', 'UTF-8');
79    }, $row);
80    fputcsv($fileHandle, $convertedRowForWrite);
81}
82fclose($fileHandle);
83
84echo "一時ファイル '{$tempFileName}' (Shift-JISエンコード) を作成しました。\n\n";
85
86// 2. 作成したShift-JISのCSVファイルを読み込み、文字化けを解決します。
87echo "CSVデータを読み込み中 (Shift-JIS -> UTF-8 変換):\n";
88$decodedCsvData = readCsvWithEncodingConversion($tempFileName, 'SJIS-win', 'UTF-8');
89
90if ($decodedCsvData !== false) {
91    // 変換されたCSVデータを表示します。
92    // 各要素がUTF-8に変換されているため、正しく表示されます。
93    foreach ($decodedCsvData as $rowNumber => $row) {
94        echo "行 " . ($rowNumber + 1) . ": ";
95        print_r($row);
96    }
97} else {
98    echo "CSVファイルの読み込み中にエラーが発生しました。\n";
99}
100
101// 3. 使用した一時ファイルを削除します。
102if (file_exists($tempFileName) && unlink($tempFileName)) {
103    echo "\n一時ファイル '{$tempFileName}' を削除しました。\n";
104} else {
105    echo "\nエラー: 一時ファイル '{$tempFileName}' の削除に失敗しました、またはファイルが存在しませんでした。\n";
106}
107
108?>

fgetcsv関数は、PHPでCSVファイルを1行ずつ読み込むための関数です。$stream引数にファイルポインタを指定し、$lengthで最大文字数(省略時は行全体)、$separatorで区切り文字(デフォルトはカンマ)、$enclosureで囲み文字(デフォルトはダブルクォーテーション)、$escapeでエスケープ文字(デフォルトはバックスラッシュ)を指定できます。

このサンプルコードでは、fgetcsvを使用してCSVファイルを読み込む際に発生しやすい文字化けの問題を解決する方法を示します。特に、Shift-JISなどのUTF-8以外のエンコーディングで保存されたCSVファイルを扱う場合に有効です。

readCsvWithEncodingConversion関数は、CSVファイルのパス$filePath、元のエンコーディング$fromEncoding、変換後のエンコーディング$toEncodingを指定して呼び出します。内部でfgetcsvで読み込んだ各行のデータをmb_convert_encoding関数を使って指定されたエンコーディングに変換することで、文字化けを防ぎます。変換されたデータは配列として返されます。エラーが発生した場合はfalseを返します。この関数を利用することで、文字化けを気にせずにCSVファイルを扱うことができます。

fgetcsv関数利用時の注意点です。CSVファイルの文字コードがUTF-8でない場合、文字化けが発生します。mb_convert_encoding関数を利用して、CSVファイルのエンコーディングをUTF-8に変換することで文字化けを防ぎます。サンプルコードでは、Shift-JISのCSVファイルをUTF-8に変換しています。fopenでファイルを開く際、適切なモード(例:'r'は読み込み)を指定してください。また、fgetcsvの引数lengthは、PHP 8.1以降は非推奨です。0を指定することで、行の長さを自動で判別できます。最後に、fcloseでファイルを閉じることを忘れないようにしてください。

関連コンテンツ