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

PHP IO 流與文件操作完全指南:從基礎概念到實戰代碼

     編輯:PHP 相關知識

核心概念:什麼是 IO 流?

IO 流(Input/Output Stream)是程式與外部設備(文件、網絡、標準輸入輸出)進行數據交換的抽象通道。在 PHP 中,文件操作本質上就是通過流來讀寫數據。

流的分類維度

維度類型說明
按數據單位位元組流 (Binary Stream)按字節讀寫,適用於圖片、視頻、二進制文件
字符流 (Text Stream)按字符讀寫,自動處理編碼轉換,適用於文本文件
按流向輸入流從源頭讀取數據到程式(如 fopen($file, 'r'))
輸出流從程式寫入數據到目標(如 fopen($file, 'w'))

PHP 文件操作核心函數族

1. 打開文件:fopen()

$handle = fopen(string $filename, string $mode, bool $use_include_path = false, resource $context = null): resource|false

常用模式:

模式說明指針位置截斷文件創建文件
r唯讀開頭否否
w唯寫開頭是是
a追加寫入結尾否是
r+讀寫開頭否否
w+讀寫開頭是是
a+讀寫追加結尾否是

2. 寫入數據

  • fwrite(resource $handle, string $data, int $length = ?): int|false — 寫入字符串(二進制安全)
  • fputs() — fwrite() 別名

3. 讀取數據

  • fread(resource $handle, int $length): string|false — 讀取指定長度字節
  • fgets(resource $handle, int $length = ?): string|false — 讀取一行(含換行符)
  • fgetc(resource $handle): string|false — 讀取單字符
  • feof(resource $handle): bool — 判斷是否到達文件末尾

4. 關閉與刷新

  • fclose(resource $handle): bool — 關閉流,釋放資源(會自動刷新緩衝區)
  • fflush(resource $handle): bool — 強制刷新輸出緩衝區

實戰代碼範例

範例 1:文本文件寫入(覆蓋模式)

<?php
$file = 'demo.txt';
$content = "Hello, PHP IO Stream!\n當前時間: " . date('Y-m-d H:i:s');

$fp = fopen($file, 'w'); // 覆蓋寫入
if ($fp === false) {
    die("無法打開文件: $file");
}

$bytes = fwrite($fp, $content);
echo "成功寫入 $bytes 字節";

fclose($fp); // 必須關閉
?>

範例 2:文本文件讀取(逐行處理)

<?php
$file = 'demo.txt';
$fp = fopen($file, 'r');
if ($fp === false) {
    die("文件不存在或不可讀: $file");
}

echo "<h3>文件內容:</h3>";
echo "<pre>";
while (!feof($fp)) {
    $line = fgets($fp, 1024);
    if ($line !== false) {
        echo htmlspecialchars($line);
    }
}
echo "</pre>";

fclose($fp);
?>

範例 3:二進制文件複製(圖片備份)

<?php
$src = 'logo.png';
$dest = 'logo_backup_' . date('Ymd') . '.png';

$srcFp = fopen($src, 'rb'); // 二進制讀取
$destFp = fopen($dest, 'wb'); // 二進制寫入

if (!$srcFp || !$destFp) {
    die("文件句柄創建失敗");
}

$bufferSize = 8192; // 8KB 緩衝區
while (!feof($srcFp)) {
    $chunk = fread($srcFp, $bufferSize);
    fwrite($destFp, $chunk);
}

fclose($srcFp);
fclose($destFp);
echo "二進制文件複製完成: $dest";
?>

範例 4:CSV 數據導出(實用場景)

<?php
$data = [
    ['ID', '姓名', '郵箱', '註冊時間'],
    [1, '張三', 'zhang@example.com', '2024-01-15'],
    [2, '李四', 'li@example.com', '2024-02-20'],
    [3, '王五', 'wang@example.com', '2024-03-10'],
];

$fp = fopen('users_export.csv', 'w');
// 寫入 BOM 防止 Excel 打開中文亂碼
fwrite($fp, "\xEF\xBB\xBF");

foreach ($data as $row) {
    fputcsv($fp, $row); // 自動處理逗號、引號轉義
}

fclose($fp);
echo "CSV 導出成功,共 " . (count($data) - 1) . " 條記錄";
?>

進階主題:流上下文與過濾器

1. HTTP 流上下文(模擬瀏覽器請求)

<?php
$opts = [
    'http' => [
        'method' => 'GET',
        'header' => "User-Agent: Mozilla/5.0 (compatible; PHP Bot)\r\n" .
                    "Accept: text/html,application/xhtml+xml\r\n",
        'timeout' => 10,
        'follow_location' => 1,
        'max_redirects' => 3,
    ],
    'ssl' => [
        'verify_peer' => false,
        'verify_peer_name' => false,
    ]
];

$context = stream_context_create($opts);
$content = file_get_contents('https://api.example.com/data', false, $context);

if ($content !== false) {
    $data = json_decode($content, true);
    print_r($data);
}
?>

2. 流過濾器(實時數據轉換)

<?php
// 註冊自定義過濾器:大寫轉換
class StrToUpperFilter extends php_user_filter {
    public function filter($in, $out, &$consumed, $closing) {
        while ($bucket = stream_bucket_make_writeable($in)) {
            $bucket->data = strtoupper($bucket->data);
            $consumed += $bucket->datalen;
            stream_bucket_append($out, $bucket);
        }
        return PSFS_PASS_ON;
    }
}
stream_filter_register('str.toupper', 'StrToUpperFilter');

// 使用過濾器
$fp = fopen('input.txt', 'r');
stream_filter_append($fp, 'str.toupper');
while (!feof($fp)) {
    echo fgets($fp);
}
fclose($fp);
?>

常見錯誤與最佳實踐

問題原因解決方案
權限拒絕目錄/文件無寫入權限檢查 PHP 進程用戶權限,Linux 下 chown www:www /path
大文件讀取記憶體溢出一次性 file_get_contents() 大文件改用 fread() 分塊讀取,或生成器逐行處理
Windows 換行符混亂\r\n vs \n使用 fgets() 自動處理,或寫入前統一 PHP_EOL
編碼亂碼文件編碼與腳本編碼不一致明確指定 mb_convert_encoding(),或使用 fopen($file, 'r', false, stream_context_create(['encoding'=>'UTF-8']))
句柄洩漏異常分支未 fclose()使用 try/finally 或 register_shutdown_function() 保證關閉

現代替代方案:SplFileObject

PHP 5.1+ 提供面向對象的文件操作類,更優雅、安全:

<?php
$file = new SplFileObject('data.csv', 'r');
$file->setFlags(SplFileObject::READ_CSV | SplFileObject::READ_AHEAD | SplFileObject::SKIP_EMPTY);

foreach ($file as $row) {
    // $row 已自動解析為數組
    printf("ID: %s, Name: %s, Email: %s\n", $row[0], $row[1], $row[2]);
}

// 寫入示例
$out = new SplFileObject('output.txt', 'w');
$out->fwrite("第一行\n");
$out->fwrite("第二行\n");
// 自動關閉(析構函數)
?>

總結

掌握 PHP IO 流機制,熟練使用 fopen/fread/fwrite/fclose 核心族群,理解流模式、緩衝區、上下文與過濾器,是處理文件上傳、日誌分析、數據導入導出、爬蟲抓取等核心業務的基石。建議新項目優先考慮 SplFileObject 以獲得更好的可維護性與異常安全性。

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