← PHP文件读取函数:fread()、fgets()与fgetc()的选用逻辑 PHP文件追加写入:用fopen()的a模式实现数据持续累积 →

PHP文件写入操作:从fwrite()到file_put_contents()的实战运用

原创 2026-05-11 PHP 已有人查阅

在PHP开发中,文件写入是与文件读取同等重要的基础技能。无论是记录系统日志、生成配置文件、还是导出用户数据,都离不开对文件写入函数的熟练运用。PHP提供了fwrite()file_put_contents()两个核心写入函数,它们各有适用场景,搭配不同的文件打开模式,能覆盖绝大多数项目开发中的写入需求。下面我们围绕这两个函数,结合真实可运行的代码示例,把文件写入这件事讲透。

一、写入前的准备:fopen()的文件打开模式

在往文件里写数据之前,需要先用fopen()打开或创建文件。这里要特别注意打开模式的选择,因为它直接决定了写入行为是“追加”还是“覆盖”,以及文件不存在时是否自动创建。

PHP中和写入相关的fopen()模式主要有以下几种:

模式 含义 文件不存在时的行为
w 写入模式,清空已有内容后从头写入 尝试创建新文件
w+ 读写模式,清空已有内容 尝试创建新文件
a 追加模式,在文件末尾追加数据 尝试创建新文件
a+ 追加读写模式 尝试创建新文件
x 排他创建模式,仅用于新建文件 文件已存在则返回FALSE并报错
x+ 排他创建读写模式 文件已存在则返回FALSE并报错
c 写入模式,不清空已有内容,指针指向开头 尝试创建新文件
c+ 读写模式,不清空已有内容 尝试创建新文件

个人经验之谈:初学PHP文件操作时,我一度搞不清楚wc的区别,结果写了一个缓存系统,每次请求都用w模式打开缓存文件,把之前缓存的数据全清空了,导致缓存命中率为零。实际上,w模式会在打开文件时立即把文件截断为零长度,而c模式不会自动清空,只是把内部指针放到文件开头。如果你需要在已有文件的开头位置插入或覆盖特定字节,c模式会很有用;而绝大多数普通写入场景,w(覆盖)和a(追加)已经够用了。

用fopen()新建一个文件

<?php
// 以写入模式打开,文件不存在时会自动创建
$fileHandle = fopen("storage/data_cache.txt", "w");
fclose($fileHandle);
?>

执行完这段代码后,storage/目录下就会多出一个空的data_cache.txt文件。把这段代码放进项目里测试时,记得确认PHP对该目录有写入权限,不然会报“failed to open stream”的错误。

二、fwrite():配合文件句柄进行写入

fwrite()是PHP里比价底层的文件写入函数,使用时必须先通过fopen()拿到一个有效的文件资源句柄。函数本身简单直接——往打开的文件里写入指定字符串,写入完成后返回成功写入的字节数。

语法结构

int fwrite ( resource $handle , string $string [, int $length ] )
  • $handle:由fopen()返回的文件句柄。

  • $string:要写入的内容字符串。

  • $length:可选参数,限制这次写入的较大字节数。如果$string的长度超过$length,只会写入前$length个字节。

示例1:基本写入操作

<?php
$fp = fopen("storage/user_login_log.txt", "w");
$log_entry = "2026-05-11 | 用户ID 2088 登录成功 | IP 10.0.2.33\n";
$bytes_written = fwrite($fp, $log_entry);
echo "本次写入字节数:" . $bytes_written;
fclose($fp);
?>

执行后文件内容

2026-05-11 | 用户ID 2088 登录成功 | IP 10.0.2.33

这里返回的写入字节数在项目中很有用——如果返回值与预期不符,说明写入过程中可能出现了问题,可以在代码里做相应的异常判断。

示例2:带字节数限制的写入

<?php
$fp = fopen("storage/temp_output.txt", "w");
$content = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
// 只写入前8个字符
fwrite($fp, $content, 8);
fclose($fp);
?>

执行后文件内容

ABCDEFGH

$length参数在某些网络流操作中会派上用场,比如通过socket传输数据时分批发送。

示例3:追加模式写入

<?php
// 第一次写入
$fp = fopen("storage/daily_quotes.txt", "w");
fwrite($fp, "实践是检验真理的唯一标准。\n");
fclose($fp);

// 第二次写入,使用a模式追加
$fp = fopen("storage/daily_quotes.txt", "a");
fwrite($fp, "千里之行,始于足下。\n");
fclose($fp);
?>

执行后文件内容

实践是检验真理的唯一标准。
千里之行,始于足下。

关于fwrite()有一个值得注意的同名函数——fputs()。实际上fputs()就是fwrite()的别名,两者功能一样,参数也一致。C语言背景的开发者可能更习惯用fputs这个名字,但在PHP社区里fwrite更常见,看自己的编码习惯选择即可。

三、file_put_contents():一步到位的写入方式

如果说fwrite()是手动挡,那file_put_contents()就是自动挡。它把文件打开、写入、关闭这三步操作封装到一个函数里,代码量大幅减少。对于中小型文件的一次性写入,这个函数通常是优先选择。

语法结构

int file_put_contents ( string $filename , mixed $data [, int $flags = 0 [, resource $context ]] )
  • $filename:目标文件路径,不存在时会尝试创建。

  • $data:要写入的数据,可以是字符串、一维数组,甚至是流资源。

  • $flags:可选标志位,控制写入行为。

  • $context:可选,用于修改流行为的上下文资源。

常用的flags标志位

  • FILE_APPEND:在文件末尾追加数据,不清空原有内容。

  • LOCK_EX:写入时获取文件排他锁,防止并发写入导致数据错乱。

  • FILE_USE_INCLUDE_PATH:在include_path目录中搜索文件。

示例1:基本写入

<?php
$result = file_put_contents("storage/version_info.txt", "当前版本:v2.8.3\n发布日期:2026-05-11\n");
echo "写入结果:" . $result . " 字节";
?>

执行后文件内容

当前版本:v2.8.3
发布日期:2026-05-11

示例2:追加模式写入

<?php
// 先写入初始内容
file_put_contents("storage/update_log.txt", "[2026-05-10] 系统初始化部署完成\n");

// 追加新记录,注意加上FILE_APPEND标志
file_put_contents("storage/update_log.txt", "[2026-05-11] 修复用户头像上传问题\n", FILE_APPEND);
?>

执行后文件内容

[2026-05-10] 系统初始化部署完成
[2026-05-11] 修复用户头像上传问题

如果不加FILE_APPEND,第二次调用会覆盖第一次的内容,这是很多新手容易忽略的细节。

示例3:结合LOCK_EX防止并发写入冲突

<?php
$visitor_data = "访客IP: 192.168.1.50 | 访问时间: 2026-05-11\n";
file_put_contents("storage/site_visits.txt", $visitor_data, FILE_APPEND | LOCK_EX);
?>

在高并发场景下(如多个请求同时写入同一个计数文件),加上LOCK_EX标志能确保同一时刻只有一个进程在写入,避免数据交叉覆盖。

四、覆盖写入与追加写入的对比

理解“覆盖”和“追加”的区别,是掌握PHP文件写入的关键节点。用w模式或file_put_contents()不加FILE_APPEND,每次写入都会清空文件原有内容;用a模式或加上FILE_APPEND,内容会被添加到文件末尾。

覆盖写入示例

<?php
$fp = fopen("storage/status_report.txt", "w");
fwrite($fp, "第一轮数据写入\n");
fclose($fp);

// 以w模式再次打开并写入
$fp = fopen("storage/status_report.txt", "w");
fwrite($fp, "第二轮数据写入(覆盖了第一轮)\n");
fclose($fp);
?>

执行后文件内容

第二轮数据写入(覆盖了第一轮)

可以看到,第一次写入的内容被清空替换。

个人建议:在记录错误日志这种需要长期累积数据的场景,务必使用a追加模式或FILE_APPEND标志。我刚接触PHP时,用w模式写了个简单的错误日志系统,结果每次新错误都把旧日志清掉了,排查线上问题时只能看到最近的报错记录,吃了不小的亏。如果是写入重要的业务数据,建议在写入前后加上文件锁(可以组合使用flock()函数),并在写入完成后校验文件内容的正确性。

五、fwrite()与file_put_contents()的选型思路

可能有人会问:既然file_put_contents()这么方便,为什么还要用fwrite()?这其实涉及到一个粒度控制的问题。

用fwrite()更合适的情况:

  • 需要多次、分批往同一个文件句柄写入数据(比如循环写入大量行)。

  • 需要精确控制文件指针位置(配合fseek()使用)。

  • 需要更细粒度的锁控制,配合flock()做复杂的事务性写入。

用file_put_contents()更合适的情况:

  • 一次性写入完整内容,代码简洁优先。

  • 写入频率不高的小型数据文件。

  • 需要结合LOCK_EX做简单的并发写入保护。

我的实战经验:写数据导出功能时,如果导出的是几万行以上的CSV文件,我会用fopen()+fwrite()逐行写入,不会先把所有数据拼成一个大字符串再用file_put_contents(),因为后者在数据量大时会占用大量内存。反过来,如果只是保存用户提交的一段配置JSON到文件,file_put_contents()一行代码搞定,比fopen()fwrite()fclose()三连调用省事多了,代码可读性也更好。

本节课程知识要点

  • 进行文件写入操作前,务必确认fopen()的打开模式是w(覆盖)、a(追加)还是c(不清空自由定位),模式选错会导致数据丢失或写入位置偏移。

  • fwrite()通过文件句柄写入,返回实际写入字节数,适合需要分批写入或精确控制指针的场景。

  • fputs()fwrite()的别名,功能一致,可按个人编码习惯选用。

  • file_put_contents()封装了打开、写入、关闭全流程,适合一次性写入,搭配FILE_APPENDLOCK_EX标志使用更安全。

  • 追加写入用FILE_APPENDa模式,覆盖写入用w模式或去掉追加标志,这是决定数据留存还是被清空的核心开关。

  • 处理高并发写入时,结合文件锁机制(LOCK_EXflock())能避免数据交叉覆盖的问题。

  • 大数据量写入优先使用流式写入(fopen()+fwrite()循环),避免将所有数据一次性加载到内存中。

← PHP文件读取函数:fread()、fgets()与fgetc()的选用逻辑 PHP文件追加写入:用fopen()的a模式实现数据持续累积 →
分享笔记 (共有 篇笔记)
验证码:
微信公众号