PHPでは、テキストファイルを読み込むための方法が複数用意されています。
代表的な方法は、以下のとおりです。
| 方法 | 主な用途 |
|---|---|
file_get_contents() | ファイル全体を文字列として読み込む |
file() | ファイルを1行ずつ配列として読み込む |
fopen() + fgets() | 大きなファイルを1行ずつ読み込む |
SplFileObject | オブジェクト指向でファイルを扱う |
fgetcsv() | CSVファイルを1行ずつ読み込む |
readfile() | ファイル内容をそのまま出力する |
小さなテキストファイルを読み込むだけなら、file_get_contents() が最もシンプルです。
一方で、ログファイルや大量データのようにファイルサイズが大きい場合は、ファイル全体を一度に読み込まず、fopen() と fgets() を使って1行ずつ処理する方法が向いています。
また、読み込んだテキストをHTMLに表示する場合は、セキュリティ対策として htmlspecialchars() によるエスケープ処理も重要です。
file_get_contents()でファイル全体を読み込む
file_get_contents()の基本的な使い方
file_get_contents() は、ファイル全体を1つの文字列として読み込む関数です。
たとえば、sample.txt というテキストファイルを読み込む場合は、以下のように書きます。
<?php
$text = file_get_contents('sample.txt');
echo $text;
sample.txt の内容が以下だったとします。
こんにちは
PHPのファイル読み込みです
この場合、file_get_contents() によってファイル全体が文字列として読み込まれます。
こんにちは
PHPのファイル読み込みです
file_get_contents() は、以下のような用途に向いています。
| 用途 | 例 |
|---|---|
| 小さなテキストファイルの読み込み | お知らせ文、説明文、テンプレート |
| JSONファイルの読み込み | 設定ファイル、保存済みデータ |
| HTML断片の読み込み | 共通パーツ、メールテンプレート |
| Markdownファイルの読み込み | 記事本文、ドキュメント |
ファイル全体をまとめて読み込めるため、コードが短くなりやすく、初心者にも扱いやすい方法です。
ただし、ファイルサイズが大きい場合は、ファイル全体をメモリに読み込むため注意が必要です。
大容量のログファイルや数十万行のデータを処理する場合は、後述する fopen() と fgets() を使った方法の方が安全です。
読み込み失敗時はfalseを確認する
file_get_contents() は、ファイルの読み込みに失敗すると false を返します。
そのため、実務では以下のように戻り値を確認することが大切です。
<?php
$path = 'sample.txt';
$text = file_get_contents($path);
if ($text === false) {
echo 'ファイルの読み込みに失敗しました。';
exit;
}
echo $text;
ここで重要なのは、比較に === false を使うことです。
if ($text === false) {
// 読み込み失敗時の処理
}
以下のように == false で判定するのは避けた方がよいです。
if ($text == false) {
// 非推奨
}
== は型を厳密に比較しないため、ファイルの内容が空文字列や "0" のような値だった場合に、意図しない判定になる可能性があります。
そのため、PHPでファイル読み込みの成否を確認する場合は、=== false のように厳密比較を使いましょう。
__DIR__を使ってファイルパスを指定する
PHPでファイルを読み込むときは、ファイルパスの指定にも注意が必要です。
たとえば、以下のように書いた場合、
$text = file_get_contents('sample.txt');
sample.txt は、PHPスクリプトの実行時のカレントディレクトリを基準に探されます。
環境や実行方法によって基準となる場所が変わると、ファイルが見つからない原因になります。
そのため、実務では __DIR__ を使って、現在のPHPファイルがあるディレクトリを基準に指定する方法がよく使われます。
<?php
$path = __DIR__ . '/sample.txt';
$text = file_get_contents($path);
if ($text === false) {
echo 'ファイルの読み込みに失敗しました。';
exit;
}
echo $text;
__DIR__ を使うことで、PHPファイルの場所を基準にパスを指定できます。
特に、以下のような場面では __DIR__ を使うと安全です。
| 場面 | 理由 |
|---|---|
| 複数のPHPファイルから読み込む場合 | 実行場所に依存しにくい |
| cronやCLIから実行する場合 | カレントディレクトリが変わることがある |
| includeファイル内で読み込む場合 | 相対パスのズレを防ぎやすい |
is_readable()で読み込み可能か確認する
ファイルを読み込む前に、対象ファイルが読み込み可能か確認したい場合は、is_readable() を使えます。
<?php
$path = __DIR__ . '/sample.txt';
if (!is_readable($path)) {
echo 'ファイルが存在しないか、読み込み権限がありません。';
exit;
}
$text = file_get_contents($path);
if ($text === false) {
echo 'ファイルの読み込みに失敗しました。';
exit;
}
echo $text;
file_exists() はファイルの存在確認に使えますが、存在していても読み込み権限がない場合があります。
そのため、読み込み処理では is_readable() を使うと、より目的に合った確認ができます。
ただし、is_readable() で確認した後にファイルの状態が変わる可能性もあります。
最終的には、file_get_contents() の戻り値も必ず確認しましょう。
file()でテキストファイルを配列として読み込む
file()の基本的な使い方
file() は、テキストファイルを1行ずつ配列として読み込む関数です。
<?php
$lines = file(__DIR__ . '/sample.txt');
if ($lines === false) {
echo 'ファイルの読み込みに失敗しました。';
exit;
}
print_r($lines);
たとえば、sample.txt の内容が以下だったとします。
りんご
みかん
バナナ
この場合、file() は以下のような配列を返します。
Array
(
[0] => りんご
[1] => みかん
[2] => バナナ
)
file() は、1行ごとにデータを処理したい場合に便利です。
たとえば、以下のような用途に向いています。
| 用途 | 例 |
|---|---|
| 行ごとのリストを読み込む | 商品名一覧、URL一覧、名前一覧 |
| 小さなログファイルを読む | 簡易ログの表示 |
| 設定ファイルを読む | key=value 形式の設定 |
| 1行ずつ画面に表示する | メモ、案内文、リスト |
改行を除去して読み込む
file() は、通常、各行の末尾に改行文字を含めた状態で配列を返します。
改行を除去したい場合は、FILE_IGNORE_NEW_LINES を指定します。
<?php
$lines = file(__DIR__ . '/sample.txt', FILE_IGNORE_NEW_LINES);
if ($lines === false) {
echo 'ファイルの読み込みに失敗しました。';
exit;
}
foreach ($lines as $line) {
echo htmlspecialchars($line, ENT_QUOTES, 'UTF-8') . '<br>';
}
空行も除外したい場合は、FILE_SKIP_EMPTY_LINES を組み合わせます。
<?php
$lines = file(
__DIR__ . '/sample.txt',
FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES
);
if ($lines === false) {
echo 'ファイルの読み込みに失敗しました。';
exit;
}
foreach ($lines as $line) {
echo htmlspecialchars($line, ENT_QUOTES, 'UTF-8') . '<br>';
}
このように、file() ではオプションを指定することで、読み込み方を調整できます。
file()は大きなファイルには注意する
file() は便利な関数ですが、ファイル全体を配列としてメモリに読み込みます。
そのため、以下のようなファイルには注意が必要です。
- 大容量のログファイル
- 数十万行以上のCSVファイル
- サーバーのアクセスログ
- 定期的に追記され続けるデータファイル
- サイズが不明な外部データ
ファイルサイズが小さい場合は問題ありませんが、大きなファイルで file() を使うと、メモリ使用量が増えすぎる可能性があります。
大きなファイルを処理する場合は、次に紹介する fopen() と fgets() を使って1行ずつ読み込む方法を検討しましょう。
fopen()とfgets()で1行ずつ読み込む
大きなファイルは1行ずつ読み込む
fopen() と fgets() を使うと、ファイルを1行ずつ読み込めます。
<?php
$path = __DIR__ . '/sample.txt';
$handle = fopen($path, 'r');
if ($handle === false) {
echo 'ファイルを開けませんでした。';
exit;
}
while (($line = fgets($handle)) !== false) {
echo htmlspecialchars($line, ENT_QUOTES, 'UTF-8') . '<br>';
}
fclose($handle);
この方法では、ファイル全体を一度にメモリへ読み込まず、1行ずつ処理します。
そのため、以下のようなケースに向いています。
| 用途 | 理由 |
|---|---|
| 大きなログファイル | 全体をメモリに読み込まなくてよい |
| 大量データの処理 | 1行ずつ処理できる |
| 長いテキストの解析 | 必要な行だけ処理しやすい |
| バッチ処理 | メモリ使用量を抑えやすい |
file_get_contents() や file() は簡単ですが、ファイル全体をメモリに読み込みます。
一方、fopen() と fgets() は1行ずつ処理できるため、大きなファイルでも比較的安全に扱えます。
fopen()の読み込みモード
fopen() では、第2引数にファイルを開くモードを指定します。
読み込みだけを行う場合は、基本的に r を使います。
$handle = fopen(__DIR__ . '/sample.txt', 'r');
主なモードは以下のとおりです。
| モード | 意味 |
|---|---|
r | 読み込み専用。ファイルポインタは先頭。ファイルが存在しないと失敗 |
r+ | 読み書き可能。ファイルポインタは先頭。ファイルが存在しないと失敗 |
w | 書き込み専用。既存内容は削除される。存在しない場合は作成 |
w+ | 読み書き可能。既存内容は削除される。存在しない場合は作成 |
a | 追記専用。存在しない場合は作成。書き込みは末尾に追加 |
a+ | 読み書き可能。存在しない場合は作成。書き込みは末尾に追加 |
x | 新規作成して書き込み専用。すでに存在すると失敗 |
x+ | 新規作成して読み書き可能。すでに存在すると失敗 |
c | 書き込み専用。存在しない場合は作成。既存ファイルは切り詰めない |
c+ | 読み書き可能。存在しない場合は作成。既存ファイルは切り詰めない |
テキストファイルを読み込むだけなら、まずは r を覚えておけば十分です。
ループ後に読み込みエラーを確認する
fgets() は、1行ずつ読み込む関数です。
ただし、fgets() が false を返す理由には、主に次の2つがあります。
| 理由 | 内容 |
|---|---|
| ファイル終端に到達した | 正常に最後まで読み込んだ |
| 読み込みエラーが起きた | 途中で何らかの問題が発生した |
より丁寧に処理するなら、ループ後に feof() を使って、正常にファイル終端へ到達したか確認します。
<?php
$path = __DIR__ . '/sample.txt';
$handle = fopen($path, 'r');
if ($handle === false) {
echo 'ファイルを開けませんでした。';
exit;
}
while (($line = fgets($handle)) !== false) {
echo htmlspecialchars($line, ENT_QUOTES, 'UTF-8') . '<br>';
}
if (!feof($handle)) {
echo 'ファイルの読み込み中にエラーが発生しました。';
}
fclose($handle);
初心者向けの簡単なサンプルでは省略されることも多いですが、実務で確実に処理したい場合は、ループ後のエラー確認も意識しましょう。
fclose()でファイルを閉じる
fopen() で開いたファイルは、処理が終わったら fclose() で閉じます。
fclose($handle);
PHPの処理が終了すれば多くの場合ファイルは自動的に閉じられますが、明示的に fclose() を書くことで、リソースを適切に解放できます。
特に、複数のファイルを扱う処理や長時間動作するスクリプトでは、ファイルを開きっぱなしにしないよう注意しましょう。
SplFileObjectでファイルを読み込む
SplFileObjectの基本的な使い方
SplFileObject は、ファイルをオブジェクト指向で扱うためのクラスです。
foreach を使って、ファイルを1行ずつ処理できます。
<?php
$file = new SplFileObject(__DIR__ . '/sample.txt');
foreach ($file as $line) {
echo htmlspecialchars($line, ENT_QUOTES, 'UTF-8') . '<br>';
}
SplFileObject を使うと、fopen() や fgets() を直接書かなくても、ファイルを行単位で扱いやすくなります。
オブジェクト指向の書き方に慣れている場合や、CSV処理などと組み合わせたい場合に便利です。
空行や改行を調整して読み込む
SplFileObject では、setFlags() を使って読み込み方を調整できます。
<?php
$file = new SplFileObject(__DIR__ . '/sample.txt');
$file->setFlags(
SplFileObject::DROP_NEW_LINE |
SplFileObject::SKIP_EMPTY |
SplFileObject::READ_AHEAD
);
foreach ($file as $line) {
echo htmlspecialchars($line, ENT_QUOTES, 'UTF-8') . '<br>';
}
それぞれのフラグの意味は以下です。
| フラグ | 意味 |
|---|---|
DROP_NEW_LINE | 行末の改行を取り除く |
SKIP_EMPTY | 空行をスキップする |
READ_AHEAD | 先読みを有効にする |
SKIP_EMPTY で空行をスキップしたい場合は、READ_AHEAD も併用すると期待どおりに動作しやすくなります。
CSVファイルを読み込む
fgetcsv()でCSVを読み込む
CSVファイルを読み込む場合は、fgetcsv() を使うのが一般的です。
<?php
$handle = fopen(__DIR__ . '/data.csv', 'r');
if ($handle === false) {
echo 'CSVファイルを開けませんでした。';
exit;
}
while (($row = fgetcsv($handle)) !== false) {
print_r($row);
}
fclose($handle);
たとえば、data.csv の内容が以下だったとします。
name,email
田中,tanaka@example.com
佐藤,sato@example.com
fgetcsv() は1行ずつCSVを読み込み、各列を配列として返します。
Array
(
[0] => name
[1] => email
)
Array
(
[0] => 田中
[1] => tanaka@example.com
)
Array
(
[0] => 佐藤
[1] => sato@example.com
)
CSVを扱う場合、単純に explode(',', $line) で分割するのはおすすめしません。
たとえば、以下のようなCSVがあるとします。
name,message
田中,"こんにちは, PHP"
この場合、メッセージの中にカンマが含まれています。
explode(',', $line) で単純に分割すると、値の中のカンマまで区切り文字として扱ってしまうため、正しく処理できません。
CSVファイルは、fgetcsv() や SplFileObject::fgetcsv() を使って読み込むのが安全です。
ヘッダー行を使って連想配列にする
CSVの1行目をヘッダーとして扱い、2行目以降を連想配列として処理することもできます。
<?php
$handle = fopen(__DIR__ . '/data.csv', 'r');
if ($handle === false) {
echo 'CSVファイルを開けませんでした。';
exit;
}
$header = fgetcsv($handle);
if ($header === false) {
echo 'CSVが空です。';
fclose($handle);
exit;
}
while (($row = fgetcsv($handle)) !== false) {
if (count($header) !== count($row)) {
continue;
}
$data = array_combine($header, $row);
echo htmlspecialchars($data['name'], ENT_QUOTES, 'UTF-8') . ':'
. htmlspecialchars($data['email'], ENT_QUOTES, 'UTF-8') . '<br>';
}
fclose($handle);
このコードでは、最初の行をヘッダーとして取得しています。
$header = fgetcsv($handle);
そして、2行目以降のデータ行を array_combine() で連想配列にしています。
$data = array_combine($header, $row);
ただし、ヘッダーの列数とデータ行の列数が一致しない場合、array_combine() は正しく処理できません。
そのため、以下のように列数を確認してから処理しています。
if (count($header) !== count($row)) {
continue;
}
CSVファイルは、手作業で編集されたり、Excelから出力されたりすることも多いため、列数の不一致を考慮しておくと安全です。
CSVの文字コードに注意する
日本語のCSVを扱う場合は、文字コードにも注意が必要です。
特に、Excel由来のCSVでは、Shift_JIS系の文字コードで保存されていることがあります。
Webページ側がUTF-8なのに、CSVファイルがShift_JIS系の文字コードになっていると、文字化けする可能性があります。
ファイルがShift_JIS系だと分かっている場合は、必要に応じてUTF-8へ変換します。
<?php
$text = file_get_contents(__DIR__ . '/sample_sjis.txt');
if ($text === false) {
echo 'ファイルの読み込みに失敗しました。';
exit;
}
$text = mb_convert_encoding($text, 'UTF-8', 'SJIS-win');
echo nl2br(htmlspecialchars($text, ENT_QUOTES, 'UTF-8'));
ここで重要なのは、変換元の文字コードを正しく把握することです。
ファイルがUTF-8なのに SJIS-win として変換すると、かえって文字化けする可能性があります。
そのため、文字コード変換は「対象ファイルがどの文字コードで保存されているか」を確認したうえで行いましょう。
readfile()でファイル内容をそのまま出力する
readfile()の基本的な使い方
readfile() は、ファイル内容を読み込み、そのまま出力する関数です。
<?php
$result = readfile(__DIR__ . '/sample.txt');
if ($result === false) {
echo 'ファイルの出力に失敗しました。';
}
readfile() は、読み込んだ内容を変数に代入して加工するのではなく、直接出力したい場合に使います。
たとえば、以下のような用途が考えられます。
| 用途 | 例 |
|---|---|
| テキストファイルをそのまま表示する | 簡単な文書の表示 |
| ダウンロード処理 | ファイルをブラウザへ送信 |
| 静的ファイルの出力 | 生成済みファイルの配信 |
ただし、HTMLページの中にテキストとして安全に表示したい場合は注意が必要です。
HTML表示ではそのまま出力しない
readfile() は、ファイル内容をそのまま出力します。
そのため、ファイル内にHTMLタグやJavaScriptが含まれていると、ブラウザ上でHTMLとして解釈される可能性があります。
たとえば、テキストファイルに以下のような内容が含まれていたとします。
<script>alert('test');</script>
この内容をそのままHTMLページに出力すると、スクリプトとして実行される危険があります。
HTMLページ内でテキストとして安全に表示したい場合は、file_get_contents() で読み込んでから htmlspecialchars() でエスケープする方が安全です。
<?php
$path = __DIR__ . '/sample.txt';
$text = file_get_contents($path);
if ($text === false) {
echo 'ファイルの読み込みに失敗しました。';
exit;
}
echo nl2br(htmlspecialchars($text, ENT_QUOTES, 'UTF-8'));
単純にファイル内容を出力するだけなら readfile() は便利ですが、HTML表示ではセキュリティ面を考慮して使い分けましょう。
読み込んだテキストをHTMLに表示する際の注意点
htmlspecialchars()でエスケープする
テキストファイルの内容をHTMLに表示する場合は、htmlspecialchars() を使ってエスケープします。
<?php
$text = file_get_contents(__DIR__ . '/sample.txt');
if ($text === false) {
echo 'ファイルの読み込みに失敗しました。';
exit;
}
echo htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
htmlspecialchars() を使うことで、HTMLタグとして解釈される可能性のある文字を安全な形に変換できます。
たとえば、以下のような文字が含まれている場合でも、安全に表示しやすくなります。
| 文字 | 変換後の例 |
|---|---|
< | < |
> | > |
& | & |
" | " |
' | ' |
外部から編集されるテキストファイル、管理画面で更新されるお知らせ文、ユーザー投稿を保存したファイルなどを表示する場合は、必ずエスケープ処理を行いましょう。
改行を反映するにはnl2br()を使う
テキストファイル内に改行があっても、HTML上ではそのまま改行として表示されません。
たとえば、以下のように出力した場合、
echo htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
ブラウザ上では、改行が期待どおりに反映されないことがあります。
改行をHTML上でも表示したい場合は、nl2br() を使います。
echo nl2br(htmlspecialchars($text, ENT_QUOTES, 'UTF-8'));
このように書くことで、テキスト内の改行が <br> に変換され、ブラウザ上でも改行として表示されます。
安全な表示の基本形は、以下のように覚えるとよいです。
echo nl2br(htmlspecialchars($text, ENT_QUOTES, 'UTF-8'));
文字コードをUTF-8に統一する
テキストファイルを読み込んだときに文字化けする場合、文字コードの不一致が原因になっていることがあります。
よくある組み合わせは以下です。
| ファイル側の文字コード | HTML側の文字コード | 起きやすい問題 |
|---|---|---|
| Shift_JIS | UTF-8 | 日本語が文字化けする |
| EUC-JP | UTF-8 | 日本語が文字化けする |
| UTF-8 | Shift_JIS | 日本語が文字化けする |
現在のWeb制作では、基本的にUTF-8へ統一するのがおすすめです。
HTMLでは、以下のように文字コードを指定します。
<meta charset="UTF-8">
PHP側でも、必要に応じてレスポンスヘッダーを指定します。
<?php
header('Content-Type: text/html; charset=UTF-8');
$text = file_get_contents(__DIR__ . '/sample.txt');
if ($text === false) {
echo 'ファイルの読み込みに失敗しました。';
exit;
}
echo nl2br(htmlspecialchars($text, ENT_QUOTES, 'UTF-8'));
文字化けを防ぐには、以下をそろえることが重要です。
| 確認項目 | 内容 |
|---|---|
| PHPファイルの文字コード | UTF-8で保存する |
| 読み込むテキストファイル | UTF-8で保存する |
| HTMLの文字コード指定 | <meta charset="UTF-8"> を指定する |
| PHPの出力文字コード | 必要に応じてヘッダーを指定する |
改行コードの違いに注意する
OSによって改行コードが異なる
テキストファイルでは、OSによって改行コードが異なる場合があります。
| OS・環境 | 改行コード |
|---|---|
| Windows | CRLF:\r\n |
| macOS / Linux | LF:\n |
| 古いMac | CR:\r |
通常の読み込みでは大きな問題にならないことも多いですが、文字列処理や行分割を行う場合には注意が必要です。
たとえば、単純に explode("\n", $text) で分割した場合、Windows由来の改行では行末に \r が残ることがあります。
改行コードを統一してから処理する
改行コードを統一したい場合は、以下のように置換できます。
<?php
$text = file_get_contents(__DIR__ . '/sample.txt');
if ($text === false) {
echo 'ファイルの読み込みに失敗しました。';
exit;
}
$text = str_replace(["\r\n", "\r"], "\n", $text);
$lines = explode("\n", $text);
foreach ($lines as $line) {
echo htmlspecialchars($line, ENT_QUOTES, 'UTF-8') . '<br>';
}
このコードでは、Windowsの \r\n と古いMacの \r を、Linux系の \n に統一しています。
$text = str_replace(["\r\n", "\r"], "\n", $text);
その後、\n で分割することで、改行コードの違いによる処理のズレを減らせます。
ユーザー入力をファイルパスに使う場合の注意点
ユーザー入力をそのまま読み込まない
ユーザー入力をそのままファイルパスに使うのは危険です。
たとえば、以下のようなコードは避けるべきです。
<?php
$file = $_GET['file'];
$text = file_get_contents($file);
echo $text;
このコードでは、URLパラメータで指定されたファイルをそのまま読み込んでいます。
たとえば、以下のようなアクセスをされる可能性があります。
read.php?file=../../config.php
このような攻撃は、ディレクトリトラバーサルと呼ばれます。
意図しないファイルを読み込まれると、設定ファイルや機密情報が漏えいする危険があります。
ホワイトリスト方式で読み込み対象を限定する
ユーザー入力に応じてファイルを切り替えたい場合は、読み込みを許可するファイルをあらかじめ決めておきます。
<?php
$allowedFiles = [
'news' => __DIR__ . '/texts/news.txt',
'about' => __DIR__ . '/texts/about.txt',
];
$key = $_GET['page'] ?? 'news';
if (!array_key_exists($key, $allowedFiles)) {
echo '指定されたファイルは読み込めません。';
exit;
}
$text = file_get_contents($allowedFiles[$key]);
if ($text === false) {
echo 'ファイルの読み込みに失敗しました。';
exit;
}
echo nl2br(htmlspecialchars($text, ENT_QUOTES, 'UTF-8'));
このコードでは、ユーザーが直接ファイルパスを指定できないようにしています。
指定できるのは、news や about など、あらかじめ許可されたキーだけです。
$allowedFiles = [
'news' => __DIR__ . '/texts/news.txt',
'about' => __DIR__ . '/texts/about.txt',
];
このようなホワイトリスト方式にすることで、意図しないファイルを読み込まれるリスクを減らせます。
重要なファイルは公開ディレクトリ外に置く
設定ファイルや管理用のテキストファイルは、Web公開ディレクトリ内に置かない方が安全です。
たとえば、以下のようなファイルには注意が必要です。
config.txtpassword.txtsecret.txtadmin_data.txt- 個人情報を含むCSVファイル
Web公開ディレクトリ内に置くと、設定によってはURLから直接アクセスされる可能性があります。
可能であれば、重要なファイルは public_html や htdocs の外側に置きましょう。
<?php
$path = dirname(__DIR__) . '/storage/config.txt';
$text = file_get_contents($path);
if ($text === false) {
echo 'ファイルの読み込みに失敗しました。';
exit;
}
公開用の文章や画像などであればWeb公開ディレクトリ内に置くこともありますが、設定値や個人情報を含むファイルは置き場所に注意が必要です。
用途別の使い分け
小さなファイルならfile_get_contents()
小さなテキストファイルを丸ごと読み込むなら、file_get_contents() が便利です。
$text = file_get_contents(__DIR__ . '/sample.txt');
コードが短く、読みやすいのがメリットです。
以下のような用途に向いています。
| 用途 | 例 |
|---|---|
| お知らせ文 | news.txt |
| 短い説明文 | message.txt |
| JSONファイル | config.json |
| HTMLテンプレート | template.html |
ただし、大きなファイルを扱う場合は、メモリ使用量に注意しましょう。
行単位で扱うならfile()
1行ずつ配列として扱いたい場合は、file() が便利です。
$lines = file(__DIR__ . '/sample.txt', FILE_IGNORE_NEW_LINES);
以下のような用途に向いています。
| 用途 | 例 |
|---|---|
| URL一覧 | 1行に1URL |
| 商品名リスト | 1行に1商品 |
| 名前一覧 | 1行に1名 |
| 小さな設定ファイル | 1行ごとの設定 |
ただし、file() はファイル全体を配列として読み込むため、大きなファイルには不向きです。
大きなファイルならfopen()とfgets()
大きなファイルを扱う場合は、fopen() と fgets() を使って1行ずつ読み込む方法が向いています。
$handle = fopen(__DIR__ . '/large.log', 'r');
if ($handle === false) {
exit('ファイルを開けませんでした。');
}
while (($line = fgets($handle)) !== false) {
// 1行ずつ処理する
}
fclose($handle);
以下のような用途に適しています。
| 用途 | 例 |
|---|---|
| 大容量ログ | アクセスログ、エラーログ |
| 大きなCSV | 数万行以上のデータ |
| バッチ処理 | 定期的なデータ処理 |
| ストリーム処理 | 逐次処理したいデータ |
メモリ使用量を抑えたい場合は、一括読み込みではなく、1行ずつ処理する方法を選びましょう。
CSVならfgetcsv()
CSVファイルを読み込む場合は、fgetcsv() を使いましょう。
$handle = fopen(__DIR__ . '/data.csv', 'r');
while (($row = fgetcsv($handle)) !== false) {
// CSVの1行を配列として処理する
}
fclose($handle);
CSVには、以下のような特殊なケースがあります。
- 値の中にカンマが含まれる
- 値がダブルクォートで囲まれている
- 空の項目がある
- 列数が行によって異なる
- 文字コードがUTF-8ではない
そのため、単純に explode() で分割するのではなく、CSV用の関数を使う方が安全です。
実務で使いやすいサンプルコード
テキストファイルを安全にHTML表示する
以下は、テキストファイルを読み込み、HTML上に安全に表示する基本形です。
<?php
$path = __DIR__ . '/message.txt';
if (!is_readable($path)) {
echo 'ファイルが存在しないか、読み込み権限がありません。';
exit;
}
$text = file_get_contents($path);
if ($text === false) {
echo 'ファイルの読み込みに失敗しました。';
exit;
}
echo nl2br(htmlspecialchars($text, ENT_QUOTES, 'UTF-8'));
このコードでは、以下の処理を行っています。
| 処理 | 目的 |
|---|---|
__DIR__ | パスを安定させる |
is_readable() | 読み込み可能か確認する |
file_get_contents() | ファイル全体を読み込む |
=== false | 読み込み失敗を厳密に判定する |
htmlspecialchars() | HTMLとして解釈されないようにする |
nl2br() | 改行をブラウザ上で反映する |
テキストファイルの内容を画面に表示する場合は、この形を基本として覚えておくとよいでしょう。
key=value形式の設定ファイルを読み込む
簡単な設定ファイルをテキストで管理したい場合、以下のような形式を使うことがあります。
site_name=サンプルサイト
admin_email=admin@example.com
items_per_page=10
このような key=value 形式のファイルは、以下のように読み込めます。
<?php
$path = __DIR__ . '/config.txt';
$lines = file($path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
if ($lines === false) {
echo '設定ファイルを読み込めませんでした。';
exit;
}
$config = [];
foreach ($lines as $line) {
if (strpos($line, '=') === false) {
continue;
}
[$key, $value] = explode('=', $line, 2);
$config[trim($key)] = trim($value);
}
echo htmlspecialchars($config['site_name'], ENT_QUOTES, 'UTF-8');
ポイントは、explode() の第3引数に 2 を指定していることです。
[$key, $value] = explode('=', $line, 2);
これにより、最初の = だけで分割できます。
値の中に = が含まれている場合でも、意図しない分割を防ぎやすくなります。
小さなログファイルの最新100行を表示する
小規模なログファイルであれば、file() と array_slice() を使って最新100行を表示できます。
<?php
$path = __DIR__ . '/app.log';
$lines = file($path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
if ($lines === false) {
echo 'ログを読み込めませんでした。';
exit;
}
$latestLines = array_slice($lines, -100);
foreach ($latestLines as $line) {
echo htmlspecialchars($line, ENT_QUOTES, 'UTF-8') . '<br>';
}
この方法は、ファイル全体を配列として読み込んでから、最後の100行を取り出しています。
$latestLines = array_slice($lines, -100);
ただし、この方法は小さなログファイル向けです。
ログファイルが大きい場合、file() で全体を読み込むとメモリを多く使います。
大きなログを扱う場合は、ファイル末尾から効率よく読む処理や、ログ管理ツールの利用も検討しましょう。
よくあるエラーと対処法
failed to open streamが表示される
PHPでファイルを読み込むとき、以下のようなエラーが表示されることがあります。
Warning: file_get_contents(sample.txt): Failed to open stream
主な原因は以下です。
| 原因 | 対処法 |
|---|---|
| ファイルが存在しない | ファイル名と配置場所を確認する |
| パスが間違っている | __DIR__ を使って指定する |
| 読み込み権限がない | パーミッションを確認する |
| 大文字小文字が違う | Linux環境では特に注意する |
| ディレクトリを指定している | ファイルパスを指定する |
特に、相対パスのズレはよくある原因です。
以下のように __DIR__ を使うと、PHPファイルの場所を基準に指定できます。
$path = __DIR__ . '/sample.txt';
文字化けする
テキストファイルを読み込んだときに文字化けする場合は、文字コードの不一致を疑いましょう。
確認すべきポイントは以下です。
| 確認項目 | 内容 |
|---|---|
| テキストファイルの文字コード | UTF-8か、Shift_JISか |
| PHPファイルの文字コード | UTF-8で保存されているか |
| HTMLの文字コード指定 | <meta charset="UTF-8"> があるか |
| PHPのレスポンスヘッダー | 必要に応じてUTF-8を指定しているか |
基本的には、PHPファイル、読み込むテキストファイル、HTML出力をUTF-8に統一すると管理しやすくなります。
改行が表示されない
テキストファイル内では改行されているのに、ブラウザで表示すると改行されないことがあります。
これは、HTMLでは通常の改行文字がそのまま見た目の改行として反映されないためです。
改行を表示したい場合は、nl2br() を使います。
echo nl2br(htmlspecialchars($text, ENT_QUOTES, 'UTF-8'));
このように、htmlspecialchars() で安全に変換したうえで、nl2br() によって改行を反映するとよいです。
PHPでテキストファイルを読み込むときのポイント
ファイルサイズに応じて読み込み方法を変える
PHPでテキストファイルを読み込むときは、ファイルサイズに応じて方法を使い分けることが大切です。
| ファイルサイズ・用途 | おすすめの方法 |
|---|---|
| 小さなファイルを丸ごと読む | file_get_contents() |
| 小さなファイルを行単位で読む | file() |
| 大きなファイルを読む | fopen() + fgets() |
| CSVを読む | fgetcsv() |
| オブジェクト指向で扱う | SplFileObject |
| そのまま出力する | readfile() |
初心者のうちは、まず file_get_contents() と file() を覚えると理解しやすいです。
その後、大きなファイルを扱う必要が出てきたら fopen() と fgets()、CSVを扱う場合は fgetcsv() を使うようにするとよいでしょう。
HTML出力時は安全性を意識する
テキストファイルの内容をブラウザに表示する場合は、内容をそのまま出力しないようにしましょう。
危険な例は以下です。
echo $text;
安全性を高めるなら、以下のようにします。
echo nl2br(htmlspecialchars($text, ENT_QUOTES, 'UTF-8'));
特に、以下のようなテキストを扱う場合は注意が必要です。
- 管理画面から編集されるお知らせ文
- ユーザー投稿を保存したテキスト
- 外部システムから取得したテキスト
- CSVから読み込んだデータ
- アップロードされたファイルの内容
自分で作成したテキストファイルであっても、将来的に編集者が増えたり、外部データを取り込んだりする可能性があります。
そのため、HTMLに表示する場合は、原則としてエスケープ処理を行うと安全です。
ユーザー入力を扱う場合はパスを制限する
ユーザー入力によって読み込むファイルを切り替える場合は、ファイルパスを直接指定させないことが重要です。
危険な書き方は以下です。
$file = $_GET['file'];
$text = file_get_contents($file);
安全にするには、許可したファイルだけを読み込むホワイトリスト方式にします。
$allowedFiles = [
'news' => __DIR__ . '/texts/news.txt',
'about' => __DIR__ . '/texts/about.txt',
];
ファイル読み込み処理では、便利さだけでなく、セキュリティも意識しましょう。
まとめ
PHPのテキストファイル読み込みは用途に応じて使い分ける
PHPでテキストファイルを読み込む方法には、複数の選択肢があります。
主な使い分けは以下のとおりです。
| やりたいこと | おすすめの方法 |
|---|---|
| ファイル全体を文字列として読み込みたい | file_get_contents() |
| 1行ずつ配列として扱いたい | file() |
| 大きなファイルを省メモリで処理したい | fopen() + fgets() |
| CSVファイルを読み込みたい | fgetcsv() |
| オブジェクト指向でファイルを扱いたい | SplFileObject |
| ファイル内容をそのまま出力したい | readfile() |
小さなファイルであれば、file_get_contents() や file() を使うと簡単です。
大きなファイルやログファイルを扱う場合は、fopen() と fgets() で1行ずつ処理すると、メモリ使用量を抑えられます。
CSVファイルを扱う場合は、カンマやダブルクォートを正しく処理するために、fgetcsv() を使いましょう。
また、読み込んだ内容をHTMLに表示する場合は、htmlspecialchars() でエスケープし、必要に応じて nl2br() で改行を反映することが大切です。
ファイル読み込みはシンプルに見えますが、実務ではパス指定、文字コード、ファイルサイズ、セキュリティを考慮する必要があります。
用途に合った関数を選び、安全にテキストファイルを扱いましょう。
以上、PHPでのテキストファイルの読み込みについてでした。
最後までお読みいただき、ありがとうございました。









