PHP fwrite()函数
fwrite()函数用于向打开的文件写入数据,支持二进制安全写入。它是PHP中处理文件写入的主要函数。
别名说明:fputs()是fwrite()的完全相同的别名,两者功能完全一致。
语法
int|false fwrite ( resource $handle , string $string [, int $length ] )
参数说明
| 参数 |
描述 |
handle |
文件指针资源,通常由fopen()函数创建 |
string |
要写入的字符串 |
length |
可选参数,指定最大写入字节数。如果指定,则只写入指定长度的字符串 |
返回值
示例代码
示例1:基本文件写入操作
<?php
// 以写入模式打开文件(如果文件不存在则创建,存在则清空)
$handle = fopen('example.txt', 'w');
if (!$handle) {
die('无法打开文件');
}
// 写入字符串
$string = "Hello, World!\n";
$bytes = fwrite($handle, $string);
if ($bytes === false) {
echo '写入失败';
} else {
echo "写入成功,写入字节数: $bytes<br>";
}
// 写入更多内容
fwrite($handle, "这是第二行内容\n");
fwrite($handle, "这是第三行内容\n");
fwrite($handle, "中文测试:你好,世界!\n");
// 关闭文件
fclose($handle);
echo "文件内容:<br>";
echo nl2br(htmlspecialchars(file_get_contents('example.txt')));
// 清理
unlink('example.txt');
?>
示例2:使用length参数限制写入长度
<?php
// 使用length参数控制写入长度
$handle = fopen('limited.txt', 'w');
if (!$handle) {
die('无法打开文件');
}
$text = "这是一个非常长的字符串,但是我们将只写入前一部分。";
// 只写入前10个字节(注意:中文字符可能被截断)
$bytes = fwrite($handle, $text, 10);
echo "写入字节数: $bytes<br>";
fclose($handle);
// 显示写入的内容
$content = file_get_contents('limited.txt');
echo "实际写入内容: " . htmlspecialchars($content) . "<br>";
echo "字符串长度: " . strlen($content) . " 字节<br>";
echo "注意:中文字符在UTF-8编码下通常占3个字节,所以可能显示乱码<br><br>";
// 二进制安全示例
$handle = fopen('binary.txt', 'wb');
$binaryData = "Hello\x00World"; // 包含空字符的字符串
$bytes = fwrite($handle, $binaryData);
fclose($handle);
echo "二进制数据写入: $bytes 字节<br>";
echo "文件大小: " . filesize('binary.txt') . " 字节<br>";
// 清理
unlink('limited.txt');
unlink('binary.txt');
?>
示例3:二进制文件写入
<?php
// 创建二进制文件
function createBinaryFile($filename, $data) {
$handle = fopen($filename, 'wb'); // 二进制写入模式
if (!$handle) {
return false;
}
$bytes = fwrite($handle, $data);
fclose($handle);
return $bytes;
}
// 写入二进制数据
$binaryData = pack('C*', 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x57, 0x6F, 0x72, 0x6C, 0x64);
$bytes = createBinaryFile('hello.bin', $binaryData);
if ($bytes !== false) {
echo "二进制文件创建成功,写入 $bytes 字节<br>";
// 显示二进制内容
$content = file_get_contents('hello.bin');
echo "十六进制表示: ";
for ($i = 0; $i < strlen($content); $i++) {
echo sprintf('%02X ', ord($content[$i]));
}
echo "<br>";
echo "字符串表示: " . htmlspecialchars($content) . "<br>";
}
// 创建图片文件头(示例)
function createImageHeader() {
// PNG文件头(示例)
$pngHeader = pack('C8', 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A);
$handle = fopen('fake.png', 'wb');
fwrite($handle, $pngHeader);
fclose($handle);
echo "<br>创建了PNG文件头(无效的PNG文件)<br>";
}
createImageHeader();
// 清理
if (file_exists('hello.bin')) unlink('hello.bin');
if (file_exists('fake.png')) unlink('fake.png');
?>
示例4:高性能文件写入器
<?php
class HighPerformanceWriter {
private $handle;
private $filename;
private $buffer = '';
private $bufferSize = 0;
private $maxBufferSize = 8192; // 8KB缓冲区
public function __construct($filename, $mode = 'w', $maxBufferSize = 8192) {
$this->filename = $filename;
$this->maxBufferSize = $maxBufferSize;
$this->handle = fopen($filename, $mode);
if (!$this->handle) {
throw new Exception("无法打开文件: $filename");
}
}
/**
* 写入数据(使用缓冲)
*/
public function write($data) {
$this->buffer .= $data;
$this->bufferSize += strlen($data);
// 如果缓冲区超过最大大小,刷新到文件
if ($this->bufferSize >= $this->maxBufferSize) {
$this->flush();
}
return strlen($data);
}
/**
* 写入一行
*/
public function writeLine($line) {
return $this->write($line . PHP_EOL);
}
/**
* 将缓冲区内容写入文件
*/
public function flush() {
if ($this->bufferSize > 0) {
$bytes = fwrite($this->handle, $this->buffer);
$this->buffer = '';
$this->bufferSize = 0;
return $bytes;
}
return 0;
}
/**
* 关闭文件
*/
public function close() {
// 先刷新缓冲区
$this->flush();
if ($this->handle) {
fclose($this->handle);
$this->handle = null;
}
}
/**
* 获取文件大小
*/
public function getFileSize() {
$currentPos = ftell($this->handle);
fseek($this->handle, 0, SEEK_END);
$size = ftell($this->handle);
fseek($this->handle, $currentPos);
return $size;
}
public function __destruct() {
$this->close();
}
}
// 使用示例:生成大文件
try {
$writer = new HighPerformanceWriter('large_output.txt');
echo "开始生成大文件...<br>";
$startTime = microtime(true);
// 写入10000行数据
for ($i = 1; $i <= 10000; $i++) {
$writer->writeLine("Line $i: " . str_repeat('X', 100));
// 每1000行显示一次进度
if ($i % 1000 === 0) {
$elapsed = microtime(true) - $startTime;
$speed = $i / $elapsed;
echo "已写入 $i 行,耗时: " . number_format($elapsed, 2) . " 秒,速度: " . number_format($speed, 2) . " 行/秒<br>";
}
}
// 手动刷新缓冲区(可选,析构函数会自动调用)
$writer->flush();
$writer->close();
$endTime = microtime(true);
$totalTime = $endTime - $startTime;
$fileSize = filesize('large_output.txt');
echo "<br>文件生成完成!<br>";
echo "总行数: 10000<br>";
echo "文件大小: " . number_format($fileSize) . " 字节<br>";
echo "总耗时: " . number_format($totalTime, 2) . " 秒<br>";
echo "平均速度: " . number_format(10000 / $totalTime, 2) . " 行/秒<br>";
// 清理
unlink('large_output.txt');
} catch (Exception $e) {
echo "错误: " . $e->getMessage();
}
?>
注意事项
重要提示:
- 二进制安全:fwrite()是二进制安全的,可以安全地写入包含空字符(\0)的数据
- 文件模式:确保以正确的模式打开文件。写入模式需要"w"、"a"、"r+"、"w+"、"a+"、"x"、"x+"、"c"或"c+"
- 文件指针:写入后文件指针会移动到写入内容的末尾
- 缓冲机制:默认情况下,写入操作可能是缓冲的,使用
fflush()可以强制刷新缓冲区
- 并发写入:多个进程同时写入同一文件时,需要使用
flock()进行文件锁定
- 错误处理:始终检查返回值,失败时返回false
- 长度参数:如果指定了length参数,即使string比length长,也只会写入length个字节
- 编码问题:写入包含多字节字符(如中文)时,确保文件以正确的编码处理
- 性能优化:对于大量小写入操作,考虑合并数据后一次性写入以提高性能
- 磁盘空间:确保有足够的磁盘空间用于文件写入
最佳实践
- 错误处理:始终检查fwrite()的返回值,确保写入成功
- 资源管理:使用完毕后使用
fclose()关闭文件句柄
- 大文件处理:写入大文件时,分块写入避免内存溢出
- 并发控制:多进程环境使用
flock()进行文件锁定
- 缓冲优化:频繁小写入时,考虑使用输出缓冲或自定义缓冲区
- 二进制文件:处理二进制文件时使用二进制模式(如'wb')
- 文件锁定:在写入前锁定文件,写入后释放锁
- 性能监控:对于大量写入操作,监控写入速度和资源使用
相关函数
| 函数 |
描述 |
与fwrite()的关系 |
fputs() |
写入文件(fwrite()的别名) |
功能完全相同 |
fopen() |
打开文件或URL |
创建fwrite()所需的文件句柄 |
fclose() |
关闭打开的文件指针 |
关闭fwrite()使用的文件句柄 |
file_put_contents() |
将字符串写入文件 |
更简单的文件写入函数,内部使用fwrite |
fflush() |
将缓冲内容输出到文件 |
强制刷新fwrite()的缓冲区 |
ftell() |
返回文件指针位置 |
获取fwrite()写入后的指针位置 |
fseek() |
在文件指针中定位 |
设置fwrite()的写入位置 |
常见问题
fwrite()需要先打开文件句柄,写入后需要手动关闭,适合需要精细控制的场景。
file_put_contents()直接传入文件名,自动处理打开和关闭,适合简单写入操作。
可能的原因:
1. 磁盘空间不足
2. 文件权限问题
3. 网络文件系统超时
4. 缓冲区未刷新(使用fflush()强制刷新)
1. 使用分块写入(每次写入一定大小的数据)
2. 使用缓冲区减少I/O操作次数
3. 监控内存使用,避免内存溢出
4. 考虑使用流式处理