首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP 文件讀取五大方法深度對比:fread、file_get_contents、流式讀取、file()、fgets 性能與場景全解析

     編輯:PHP 綜合知識

前言

文件讀取是 PHP 最基礎的 I/O 操作,但**「沒有最好的方法,只有最適合場景的方法」**。從一次性讀入內存到流式逐塊處理,從二進制安全到文本行處理,五大核心方法各有優勢局限。本文將從**內存佔用、執行速度、適用文件大小、編碼處理、錯誤控制**五個維度深度對比,並給出生產環境選型決策樹。

五大方法核心對比表

方法核心函數讀取模式內存佔用適用文件大小二進制安全行處理典型場景
1. freadfread($fp, $length)指定長度/全量高 (全量讀入)< 可用內存✅ 是手動分割二進制文件、小配置文件、需精確控制字節
2. file_get_contentsfile_get_contents($path)全量一次性高 (全量讀入)< 可用內存✅ 是手動分割**小文件首選**、API 響應、模板讀取、靜態資源內聯
3. 流式 fread 循環fread($fp, $buffer) + feof()固定緩衝區塊**極低 (常數)****無上限 (GB/TB)**✅ 是手動分割大文件處理、視頻流、日誌分析、加密/壓縮流
4. file()file($path)全量讀入→按行分割數組高 (全量+數組開銷)< 內存/10✅ 是✅ **原生逐行**中小文本文件、CSV/日誌逐行處理、配置解析
5. fgets()fgets($fp, $length)逐行流式讀取**低 (單行緩衝)****大文本文件**✅ 是✅ **原生逐行**超大日誌、CSV 流式解析、內存受限環境逐行處理

各方法深度解析與代碼範例

方法一:fread() - 精確字節控制

<?php
$path = 'data.bin';
$fp = fopen($path, 'rb'); // 'b' 標誌:二進制模式 (跨平台兼容)
if ($fp) {
    // 讀取前 1024 字節 (文件頭解析)
    $header = fread($fp, 1024);
    
    // 讀取整個文件 (已知大小)
    $size = filesize($path);
    $content = fread($fp, $size); // 從當前指針位置讀取
    
    // 關鍵:fread 遇到 EOF 返回空字串 "", 非 false
    fclose($fp);
}

// 二進制安全讀取圖片並輸出
header('Content-Type: image/jpeg');
echo file_get_contents('photo.jpg'); // 更簡潔
?>

優勢:精確控制讀取位置 (配合 fseek) 和長度;二進制安全。
劣勢:全量讀取大文件會 OOM;需手動管理 fopen/fclose。

方法二:file_get_contents() - 現代首選 (小文件)

<?php
// 1. 讀取本地文件
$content = file_get_contents('config.json');
$config = json_decode($content, true);

// 2. 讀取遠程 HTTP/HTTPS (需 allow_url_fopen=On)
$html = file_get_contents('https://api.example.com/data');

// 3. 上下文控制 (超時、Header、POST、代理)
$ctx = stream_context_create([
    'http' => [
        'timeout' => 10,
        'method'  => 'GET',
        'header'  => "User-Agent: MyApp/1.0\r\nAccept: application/json",
    ],
    'ssl' => ['verify_peer' => false, 'verify_peer_name' => false] // 開發環境
]);
$json = file_get_contents('https://api.example.com/data', false, $ctx);

// 4. 部分讀取 (offset, maxlen) - PHP 5.1+
$chunk = file_get_contents('huge.log', false, null, 1000, 500); // 從1000字節讀500字節
?>

優勢:代碼極簡、內部優化 (mmap)、支援流上下文 (HTTP/FTP/SSL/壓縮)、可部分讀取。
劣勢:大文件全量讀入內存 → OOM 風險;無原生逐行迭代。

方法三:流式 fread 循環 - 大文件/二進制流 處理王者

<?php
function processLargeFile(string $path, callable $chunkHandler, int $bufferSize = 8192): void {
    $fp = fopen($path, 'rb');
    if (!$fp) throw new RuntimeException("無法打開: $path");
    
    try {
        while (!feof($fp)) {
            $chunk = fread($fp, $bufferSize);
            if ($chunk === '') break; // EOF
            $chunkHandler($chunk); // 處理塊 (寫入另文件、發送網絡、哈希、解密)
        }
    } finally {
        fclose($fp);
    }
}

// 使用:計算大文件 SHA256 (不載入內存)
$hash = hash_init('sha256');
processLargeFile('video.mp4', function($chunk) use ($hash) {
    hash_update($hash, $chunk);
});
echo hash_final($hash);

// 使用:分塊發送大文件下載 (斷點續傳核心)
function sendFileChunked(string $path, int $start, int $length, int $buffer = 8192) {
    $fp = fopen($path, 'rb');
    fseek($fp, $start);
    $remaining = $length;
    while ($remaining > 0 && !feof($fp)) {
        $read = min($buffer, $remaining);
        echo fread($fp, $read);
        $remaining -= $read;
        @ob_flush(); @flush(); // 刷新輸出緩衝
    }
    fclose($fp);
}
?>

核心優勢:**內存佔用恆定 ≈ $bufferSize** (默認 8KB),可處理 TB 級文件。
關鍵細節:feof() 在**讀取嘗試後**才為 true;fread 返回 '' (空字串) 表示 EOF,非 false。

方法四:file() - 文本行數組化 (中小文件)

<?php
// 1. 直接獲取行數組 (保留換行符)
$lines = file('data.txt', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
// FILE_IGNORE_NEW_LINES: 移除每行末尾 \n/\r\n
// FILE_SKIP_EMPTY_LINES: 跳過空行

// 2. 處理 CSV (簡易)
foreach ($lines as $line) {
    $fields = str_getcsv($line); // 解析 CSV 行
    // 處理...
}

// 3. 反轉文件行序 (需全量內存)
$reversed = array_reverse($lines);
file_put_contents('reversed.txt', implode("\n", $reversed));
?>

優勢:一行代碼獲取行數組、原生支援跳過空行/去換行符。
劣勢:**內存 = 文件大小 + 數組開銷 (約 2-3x)**;大文件極易 OOM;FILE_SKIP_EMPTY_LINES 會跳過包含空白字符的行 (可能非預期)。

方法五:fgets() - 大文本文件逐行流式處理

<?php
function processLargeTextFile(string $path, callable $lineHandler, int $bufferSize = 0): void {
    $fp = fopen($path, 'r'); // 文本模式 (自動處理換行符)
    if (!$fp) throw new RuntimeException("無法打開: $path");
    
    try {
        // $bufferSize = 0 或省略 = 默認 1024 字節 (舊版) / 8192 (新版)
        // 設置較大緩衝 (如 64KB) 可減少系統調用,提升大文件速度
        while (($line = fgets($fp, $bufferSize ?: 65536)) !== false) {
            // $line 包含換行符 (\n 或 \r\n)
            $line = rtrim($line, "\r\n"); // 移除換行
            $lineHandler($line);
        }
    } finally {
        fclose($fp);
    }
}

// 使用:逐行處理 10GB 訪問日誌 (內存恆定 ~64KB)
$ipCounts = [];
processLargeTextFile('access.log', function($line) use (&$ipCounts) {
    // 簡易解析: IP - - [time] "request" ...
    if (preg_match('/^(\S+)/', $line, $m)) {
        $ipCounts[$m[1]] = ($ipCounts[$m[1]] ?? 0) + 1;
    }
}, 65536); // 64KB 緩衝
arsort($ipCounts);
print_r(array_slice($ipCounts, 0, 10));

// 使用:流式 CSV 解析 (配合 fgetcsv 更佳)
$fp = fopen('huge.csv', 'r');
while (($row = fgetcsv($fp)) !== false) {
    // $row 為字段數組
}
fclose($fp);
?>

優勢:**真正的流式逐行**,內存恆定;原生處理換行符;配合 fgetcsv 解析 CSV 完美。
劣勢:僅適用文本文件;二進制文件用 fread;極長行 (超過緩衝) 會被截斷 (需增大 $bufferSize)。

性能基準測試參考 (100MB 文本文件,PHP 8.2,SSD,內存 512MB)

方法耗時峰值內存備註
file_get_contents~120ms~100MB最快,但佔內存
file()~180ms~280MB數組開銷大
fread (8KB buffer)~250ms~2MB流式,穩定
fgets (64KB buffer)~220ms~2MB逐行流式最快
fread (1MB buffer)~150ms~3MB大緩衝接近 file_get_contents 速度

結論:
• 小文件 (<10MB):**file_get_contents 或 file()** 最快開發體驗。
• 大文件 (>50MB) / 內存受限:**fread 循環 (二進制) 或 fgets/fgetcsv (文本)** 唯一可行。
• 緩衝區大小對流式速度影響大:64KB-256KB 為甜點,平衡系統調用開銷與內存。

編碼與換行符處理最佳實踐

<?php
// 1. 統一換行符為 \n (內部處理)
$content = str_replace(["\r\n", "\r"], "\n", $content);

// 2. 輸出時轉為平台格式 (Web 通常用 \n 或 
) // PHP_EOL 為當前系統換行符 (Linux \n, Windows \r\n) file_put_contents($path, implode(PHP_EOL, $lines)); // 3. 編碼轉換 (讀取非 UTF-8 文件) $content = file_get_contents('gbk.txt'); $utf8 = iconv('GBK', 'UTF-8//IGNORE', $content); // //IGNORE 忽略非法字符 // 4. BOM 處理 (UTF-8 BOM: EF BB BF) if (strncmp($content, "\xEF\xBB\xBF", 3) === 0) { $content = substr($content, 3); } ?>

錯誤處理與資源管理模式

<?php
// 現代模式:try/finally 確保關閉
function safeRead(string $path): ?string {
    $fp = @fopen($path, 'rb');
    if (!$fp) return null; // 或拋異常
    
    try {
        // 讀取邏輯...
        $content = stream_get_contents($fp); // 從當前指針讀取剩餘 (流式全量)
        return $content;
    } finally {
        fclose($fp); // 異常也會執行
    }
}

// 流上下文超時控制 (file_get_contents/fopen 通用)
$ctx = stream_context_create(['http' => ['timeout' => 5]]);
$content = @file_get_contents('https://slow.api', false, $ctx);
if ($content === false) { /* 處理超時 */ }
?>

選型決策樹 (生產環境速查)

需讀取文件?
│
├─ 文件 < 10MB 且 內存充裕 ?
│   ├─ 是 → 需逐行處理/CSV? → file() / fgetcsv(fopen)
│   └─ 否 → 整體內容 (JSON/模板/二進制)? → file_get_contents() 【首選】
│
├─ 文件 > 50MB 或 內存受限 / 長時間運行進程 ?
│   ├─ 二進制 (圖片/視頻/壓縮包/加密)? → fread($fp, 64KB-256KB) 循環
│   └─ 文本/日誌/CSV 逐行? → fgets($fp, 64KB) 循環 / fgetcsv($fp)
│
├─ 遠程 HTTP/HTTPS ?
│   └─ file_get_contents + stream_context (超時/Header/代理) 【首選】
│
└─ 需隨機訪問/斷點續傳/部分讀取?
    └─ fopen + fseek + fread / file_get_contents($path, false, null, $offset, $maxlen)

總結

PHP 文件讀取無銀彈,**核心取捨在於「內存 vs 功能」**:

  • file_get_contents():現代小文件/網絡請求**首選**,API 簡潔、支援上下文、內部優化。
  • file():中小文本文件**逐行數組化**神器,開發效率高,但內存翻倍。
  • fread() 循環:**二進制大文件/流式處理**標準解法,緩衝區大小可調優化速度。
  • fgets()/fgetcsv():**大文本文件逐行流式**唯一正解,配合 CSV 解析完美。
  • 統一原則:fopen 必配 try/finally fclose;大文件**堅決不用**全量讀入函數;編碼統一 UTF-8,換行符內部統一 \n。

根據文件類型、大小、內存預算、處理邏輯選擇對應方法,並封裝為統一 FileReader 服務類,即可構建健壯、高效的文件讀取基礎設施。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved