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

PHP 文件讀寫與內容替換完整實戰指南

     編輯:PHP 相關知識

前言

文件操作是 PHP 後端開發基礎技能:日誌記錄、配置管理、模板渲染、靜態化生成、數據導入導出等場景無處不在。本文系統梳理 fopen 模式、讀取寫入函數、指針控制、鎖機制、編碼陷阱,並提供可直接用於生產的封裝函數。

核心函數速查

函數作用關鍵參數
fopen(filename, mode)打開文件,返回資源句柄模式詳見下表
fwrite(handle, string, length?)寫入內容(二進制安全)可選長度限制
fread(handle, length)讀取指定字節數必須指定長度
fgets(handle, length)逐行讀取(含換行符)最大長度,預設 1024
fgetcsv(handle, length, separator)解析 CSV 行自動處理引號、轉義
file_get_contents(path)一次性讀取整個文件到字串PHP 4.3+,支援 URL
file_put_contents(path, data, flags)一次性寫入(原子性可选)FILE_APPEND, LOCK_EX
fclose(handle)關閉句柄,釋放鎖必須調用
feof(handle)判斷是否到達 EOF循環讀取終止條件
flock(handle, operation)文件鎖(共享/獨佔/非阻塞)LOCK_SH, LOCK_EX, LOCK_NB

fopen 模式完整對照表

模式讀寫指針位置截斷文件文件不存在說明
r✅❌開頭否失敗只讀
r+✅✅開頭否失敗讀寫
w❌✅開頭✅創建只寫,清空內容
w+✅✅開頭✅創建讀寫,清空內容
a❌✅結尾否創建追加寫入
a+✅✅結尾否創建讀取+追加
x❌✅開頭否創建/失敗獨佔創建,存在則失敗
x+✅✅開頭否創建/失敗獨佔創建讀寫
c❌✅開頭否創建不截斷,PHP 5.2.6+
c+✅✅開頭否創建讀寫不截斷

**二進制安全**:Windows 下建議加 b 後綴(如 rb、wb),避免 \r\n 轉換導致位置偏移。

實戰範例:安全寫入、讀取替換、追加日誌

1. 原子性寫入(避免半寫損壞)

<?php
function safeWrite(string $path, string $data): bool {
    $dir = dirname($path);
    if (!is_dir($dir)) mkdir($dir, 0755, true);
    
    $tmp = $path . '.tmp.' . random_bytes(8);
    $fp = fopen($tmp, 'wb');
    if (!$fp) return false;
    
    if (flock($fp, LOCK_EX)) {
        $written = fwrite($fp, $data);
        flock($fp, LOCK_UN);
        fclose($fp);
        if ($written === false || $written !== strlen($data)) {
            @unlink($tmp);
            return false;
        }
        return rename($tmp, $path); // 原子替換
    }
    fclose($fp);
    @unlink($tmp);
    return false;
}
?>

2. 逐行讀取並替換(大文件記憶體友好)

<?php
function replaceInFile(string $path, string $search, string $replace): bool {
    $tmp = $path . '.replace.tmp';
    $rfp = fopen($path, 'rb');
    $wfp = fopen($tmp, 'wb');
    if (!$rfp || !$wfp) { @fclose($rfp); @fclose($wfp); @unlink($tmp); return false; }
    
    while (($line = fgets($rfp)) !== false) {
        fwrite($wfp, str_replace($search, $replace, $line));
    }
    fclose($rfp);
    fclose($wfp);
    return rename($tmp, $path);
}
?>

3. 高併發日誌追加(非阻塞鎖 + 旋轉)

<?php
function appendLog(string $logDir, string $message, int $maxSize = 10*1024*1024): void {
    $date = date('Y-m-d');
    $file = $logDir . "/app_{$date}.log";
    if (!is_dir($logDir)) mkdir($logDir, 0755, true);
    
    // 日誌輪轉
    if (file_exists($file) && filesize($file) > $maxSize) {
        $rotate = $logDir . "/app_{$date}_" . time() . ".log";
        rename($file, $rotate);
    }
    
    $fp = fopen($file, 'ab'); // 追加二進制
    if (!$fp) return;
    
    // 非阻塞獨佔鎖,獲取失敗直接丟棄(日誌允許丟失極少量)
    if (flock($fp, LOCK_EX | LOCK_NB)) {
        fwrite($fp, date('c') . " {$message}\n");
        flock($fp, LOCK_UN);
    }
    fclose($fp);
}
?>

常見坑與避雷指南

坑點後果解決
fopen('r') 打開不存在文件返回 false,後續 fread 崩潰先 file_exists 或檢查返回值
大文件 file_get_contents記憶體耗盡 (OOM)改用 fgets 逐行/分塊處理
Windows \r\n 換行差異fgets 讀取含 \r,邏輯判斷失效用 rtrim($line, "\r\n") 或 fopen(..., 'rb')
並發寫入無鎖數據交錯、損壞必須 flock(LOCK_EX)
長時間持有鎖併發阻塞、超時最小化臨界區、用 LOCK_NB 失敗快速返回
未 fclose句柄洩漏、鎖不釋放、數據未刷盤try/finally 或 register_shutdown_function 保證關閉
編碼不一致(UTF-8 vs GBK)中文亂碼、替換失效統一 UTF-8,或用 mb_convert_encoding 處理

現代替代:SplFileObject 物件導向封裝

<?php
// 逐行遍歷
$file = new SplFileObject('data.csv');
$file->setFlags(SplFileObject::READ_CSV);
foreach ($file as $row) {
    // $row 已解析為陣列
}

// 寫入
$file = new SplFileObject('output.txt', 'w');
$file->fwrite("第一行\n");
$file->fwrite("第二行\n");
// 自動 fclose
?>

優點:自動資源管理、內建 CSV 解析、可迭代、支援 seek() 隨機訪問。

結語

PHP 文件操作看似基礎,實則蘊含「模式選擇、鎖策略、編碼處理、大文件流式處理、原子性寫入」等工程細節。掌握 fopen 全模式、善用 flock、避開 file_get_contents 大文件陷阱、採用臨時文件+重命名實現原子寫入,是從「能跑通」到「生產可靠」的關鍵躍升。

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