運維腳本、配置管理、日誌清洗等場景常需「讀取文本文件 → 修改第 N 行 → 寫回」。PHP 無原生「隨機行寫入」函數,經典做法是「全量讀入 → 分割數組 → 修改 → 合併寫回」。
$filePath = '123.txt';
$content = file_get_contents($filePath);
// 1. 按換行符分割為數組(兼容 \r\n / \n / \r)
$lines = preg_split('/\R/', $content); // PHP 7+ 推薦
// 兼容舊版:$lines = explode("\n", $content); // 僅按 \n 分割,行尾可能殘留 \r
// 2. 替換指定行(索引從 0 開始,第 13 行對應 [12])
if (isset($lines[12])) {
$lines[12] = '123'; // 新內容
} else {
// 行號超出範圍:可選擇擴展數組或拋出異常
$lines[12] = '123';
}
// 3. 合併回字符串並寫入
$newContent = implode("\n", $lines); // 統一用 \n 存儲,跨平台兼容性最好
file_put_contents($filePath, $newContent);
\r\n、\n、\r、\f 等),PHP 7.0+ 可用,最健壯。\r\n 下會殘留 \r 在行尾,後續處理需 rtrim($line, "\r")。若目標行號大於現有行數,上述代碼會自動在中間插入空行(PHP 數組自動擴展)。若需「僅修改現有行,不存在則報錯」,請加判斷:
if (!isset($lines[$targetIndex])) {
throw new OutOfBoundsException("行號 $targetLine 不存在");
}
讀取、寫入均建議顯式指定編碼,避免中文亂碼:
$content = file_get_contents($filePath);
// 若文件為 GBK,需轉碼:$content = mb_convert_encoding($content, 'UTF-8', 'GBK');
// ... 處理 ...
file_put_contents($filePath, $newContent); // 寫入時保持 UTF-8
若文件超過百 MB,file_get_contents 會佔用大量內存甚至 OOM。改用「流式讀寫 + 臨時文件」:
$src = 'big.log';
$dst = $src . '.tmp';
$targetLine = 10000; // 要修改的行號(1-based)
$newLineContent = 'REPLACED CONTENT';
$fpIn = fopen($src, 'r');
$fpOut = fopen($dst, 'w');
$lineNo = 0;
while (($line = fgets($fpIn)) !== false) {
$lineNo++;
if ($lineNo === $targetLine) {
fwrite($fpOut, $newLineContent . "\n");
} else {
fwrite($fpOut, $line);
}
}
fclose($fpIn);
fclose($fpOut);
// 原子替換(防止讀取時文件損壞)
rename($dst, $src);
優勢: 內存佔用恆定(僅緩衝區)、支援超大文件、rename 原子性保證並發安全。
/**
* 替換文本文件指定行
* @param string $filePath 文件路徑
* @param int $lineNum 目標行號(1-based)
* @param string $newContent 新行內容(不含換行符)
* @param string $encoding 文件編碼,默認 UTF-8
* @return bool 成功返回 true,行號越界返回 false
*/
function replaceFileLine(string $filePath, int $lineNum, string $newContent, string $encoding = 'UTF-8'): bool {
if ($lineNum < 1) return false;
$lines = file($filePath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
// file() 自動處理換行符,返回數組;FLAGS 去除行尾換行符並跳過空行(視業務決定是否保留 SKIP_EMPTY_LINES)
if ($lineNum > count($lines)) return false;
$lines[$lineNum - 1] = $newContent;
return (bool) file_put_contents($filePath, implode("\n", $lines));
}
| 場景 | 推薦方案 | 核心函數 |
|---|---|---|
| 小文件(< 10MB)、簡單腳本 | 全量讀入 + preg_split/爆炸 + implode | file_get_contents, preg_split, implode, file_put_contents |
| 中大文件、長期運行服務 | 流式讀寫 + 臨時文件 + rename 原子替換 | fopen, fgets, fwrite, fclose, rename |
| 需要保留原文件格式(編碼、換行符) | 二進制模式 'rb'/'wb' + 不做編碼轉換 | fopen($f, 'rb'), fwrite 不轉碼 |
掌握「全量法」與「流式法」兩套招式,即可應對 99% 的文本行替換需求。記住:小文件圖簡潔,大文件守內存,編碼統一用 UTF-8,原子寫入防併發。