PHP: 文件操作

数据库存结构化数据,文件存非结构化数据——日志、配置文件、CSV 报表、用户上传的图片。这节课让你在 PHP 中自如操作文件和目录。

1. 读文件

PHP
<?php
// 方法一:file_get_contents() — 一行读取整个文件
$content = file_get_contents('hello.txt');
echo $content;

// 方法二:fopen + fread — 流式读取(大文件)
$handle = fopen('hello.txt', 'r');
$content = fread($handle, filesize('hello.txt'));
fclose($handle);

// 方法三:逐行读取(最省内存)
$handle = fopen('data.txt', 'r');
while (($line = fgets($handle)) !== false) {
    echo trim($line) . "<br>";
}
fclose($handle);

// 方法四:file() — 一次性读入数组(每行一个元素)
$lines = file('data.txt', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
print_r($lines);
?>
方法 适用场景 内存
file_get_contents() 小文件(配置、JSON) 一次性
fopen + fread 中等文件 受控
fopen + fgets 大文件(日志、CSV)
file() 按行处理的小文件 一次性

2. 写文件

PHP
<?php
// file_put_contents() — 一行写文件
file_put_contents('notes.txt', "今天学了 PHP 文件操作\n");

// 追加模式(第三个参数 FILE_APPEND)
file_put_contents('notes.txt', "现在会追加内容了\n", FILE_APPEND);

// 独占锁写入(防止并发冲突)
file_put_contents('counter.txt', $count, LOCK_EX);

// fwrite — 精细化控制
$handle = fopen('log.txt', 'a');  // 'a' = 追加模式
fwrite($handle, date("Y-m-d H:i:s") . " — 用户登录\n");
fclose($handle);
?>

▶ 示例:简易日志系统

PHP
<?php
class Logger {
    public function __construct(
        private string $logFile
    ) {}
    
    public function info(string $message): void {
        $this->write('INFO', $message);
    }
    
    public function error(string $message): void {
        $this->write('ERROR', $message);
    }
    
    public function warning(string $message): void {
        $this->write('WARNING', $message);
    }
    
    private function write(string $level, string $message): void {
        $time = date("Y-m-d H:i:s");
        $line = "[{$time}] [{$level}] {$message}\n";
        file_put_contents($this->logFile, $line, FILE_APPEND | LOCK_EX);
    }
    
    public function getRecent(int $lines = 20): array {
        if (!file_exists($this->logFile)) {
            return [];
        }
        
        $all = file($this->logFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
        return array_slice($all, -$lines);
    }
}

$log = new Logger('app.log');
$log->info('程序启动');
$log->warning('配置文件未找到,使用默认值');
$log->error('数据库连接超时');
$log->info('程序结束');

echo "<h3>最近日志</h3><pre>" . implode("\n", $log->getRecent(10)) . "</pre>";
?>
▶ 试一试

3. 文件检测

PHP
<?php
$file = 'test.txt';

// 存在检查
var_dump(file_exists($file));       // bool
var_dump(is_file($file));           // 且是文件(不是目录)
var_dump(is_dir($file));            // 检查是否是目录

// 读写检查
var_dump(is_readable($file));      // 能否读取
var_dump(is_writable($file));      // 能否写入

// 文件信息
echo filesize($file) . " 字节<br>";  // 大小
echo filemtime($file);               // 最后修改时间(Unix时间戳)
echo date("Y-m-d H:i:s", filemtime($file)) . "<br>";  // 转日期
echo pathinfo($file, PATHINFO_EXTENSION);  // txt
?>

▶ 示例:文件缓存

PHP
<?php
class FileCache {
    private string $cacheDir;
    
    public function __construct(string $dir = 'cache') {
        $this->cacheDir = $dir;
        if (!is_dir($this->cacheDir)) {
            mkdir($this->cacheDir, 0755, true);
        }
    }
    
    public function get(string $key, int $ttl = 3600): mixed {
        $file = "{$this->cacheDir}/{$key}.cache";
        
        if (!file_exists($file)) return null;
        if (time() - filemtime($file) > $ttl) {
            unlink($file);
            return null;
        }
        
        return unserialize(file_get_contents($file));
    }
    
    public function set(string $key, mixed $value): void {
        $file = "{$this->cacheDir}/{$key}.cache";
        file_put_contents($file, serialize($value), LOCK_EX);
    }
}

$cache = new FileCache();
$cache->set('config', ['app_name' => 'MyBlog', 'version' => '1.0']);
print_r($cache->get('config'));
?>
▶ 试一试

4. 文件操作函数速查

PHP
<?php
$src = 'source.txt';
$dst = 'backup.txt';

// 复制
copy($src, $dst);

// 重命名/移动
rename($src, 'new-name.txt');
rename('/tmp/upload.txt', '/var/www/files/upload.txt');  // 移动

// 删除
unlink('old-file.txt');

// 目录操作
mkdir('new-folder', 0755, true);  // true = 递归创建
rmdir('empty-folder');             // 只能删空目录

// 遍历目录
foreach (scandir('uploads') as $item) {
    if ($item !== '.' && $item !== '..') {
        echo "{$item} — " . (is_dir("uploads/{$item}") ? '目录' : '文件') . "<br>";
    }
}

// 用 glob 按模式查找文件
$images = glob('uploads/*.{jpg,png,gif}', GLOB_BRACE);
echo "找到 " . count($images) . " 张图片";
?>

5. CSV 读写

CSV 是数据交换的通用格式——Excel 能打开,任何语言都能读写:

PHP
<?php
// === 写 CSV ===
$data = [
    ['姓名', '年龄', '城市'],
    ['小明', 25, '北京'],
    ['小红', 22, '上海'],
    ['小刚', 28, '广州'],
];

$handle = fopen('users.csv', 'w');
// 写 UTF-8 BOM(让 Excel 正确识别中文)
fwrite($handle, "\xEF\xBB\xBF");

foreach ($data as $row) {
    fputcsv($handle, $row);
}
fclose($handle);
echo "CSV 文件已生成 ✅<br>";

// === 读 CSV ===
$handle = fopen('users.csv', 'r');
// 跳过 BOM
fseek($handle, 3);

// 读表头
$headers = fgetcsv($handle);

while (($row = fgetcsv($handle)) !== false) {
    // 用表头当键
    $item = array_combine($headers, $row);
    echo "{$item['姓名']},{$item['年龄']}岁,在{$item['城市']}<br>";
}
fclose($handle);
?>

▶ 示例:CSV 数据导出

PHP
<?php
function exportToCSV(array $data, string $filename): void {
    header('Content-Type: text/csv; charset=utf-8');
    header("Content-Disposition: attachment; filename={$filename}");
    
    $output = fopen('php://output', 'w');
    fwrite($output, "\xEF\xBB\xBF");  // BOM
    
    // 表头
    if (!empty($data)) {
        fputcsv($output, array_keys($data[0]));
    }
    
    // 数据
    foreach ($data as $row) {
        fputcsv($output, $row);
    }
    fclose($output);
}
// exportToCSV($usersData, 'users_export.csv');
?>
▶ 试一试

6. 文件锁定(防并发写)

PHP
<?php
class Counter {
    private string $file;
    
    public function __construct(string $file = 'counter.dat') {
        $this->file = $file;
        if (!file_exists($this->file)) {
            file_put_contents($this->file, 0);
        }
    }
    
    public function increment(): int {
        $handle = fopen($this->file, 'c+');
        
        // 获取排他锁(其他进程等待)
        if (flock($handle, LOCK_EX)) {
            $count = (int)fread($handle, 100);
            $count++;
            
            // 回到文件开头
            fseek($handle, 0);
            ftruncate($handle, 0);  // 清空
            fwrite($handle, (string)$count);
            
            flock($handle, LOCK_UN);  // 解锁
            fclose($handle);
            return $count;
        }
        fclose($handle);
        return -1;
    }
}
?>

❓ 常见问题

Q file_get_contents()fopen + fgets 怎么选?
A 小文件(<1MB)用 file_get_contents(),一行搞定。大文件(日志数万行)用 fgets() 逐行读,内存友好。未知大小先用 fopen
Q 中文在 Excel 中乱码?
A CSV 文件开头加 UTF-8 BOM("\xEF\xBB\xBF")解决。Excel 默认按本地编码读取,BOM 告诉它这是 UTF-8。
Q 文件锁定 flock 是真的必须的吗?
A 多人同时读写同一文件时必须有。计数器、缓存、日志等场景如果不加锁,可能读到的值已被其他进程修改(竞态条件)。

📖 小节

📝 作业

  1. 写一个访客计数器:每次刷新页面,数字 +1,用文件存储计数。
  2. 写一个简易留言板:表单提交留言→追加写入文件→从文件读取展示所有留言(附时间戳)。
  3. 把用户在数据库中导出为 CSV 文件(从 MySQL 读数据 → 生成 CSV 文件 → 提供下载链接)。
Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏