PHP fwrite()函数

fwrite()函数用于向打开的文件写入数据,支持二进制安全写入。它是PHP中处理文件写入的主要函数。
别名说明:fputs()fwrite()的完全相同的别名,两者功能完全一致。

语法

int|false fwrite ( resource $handle , string $string [, int $length ] )

参数说明

参数 描述
handle 文件指针资源,通常由fopen()函数创建
string 要写入的字符串
length 可选参数,指定最大写入字节数。如果指定,则只写入指定长度的字符串

返回值

  • 成功时返回写入的字节数
  • 失败时返回false

示例代码

示例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个字节
  • 编码问题:写入包含多字节字符(如中文)时,确保文件以正确的编码处理
  • 性能优化:对于大量小写入操作,考虑合并数据后一次性写入以提高性能
  • 磁盘空间:确保有足够的磁盘空间用于文件写入

最佳实践

  1. 错误处理:始终检查fwrite()的返回值,确保写入成功
  2. 资源管理:使用完毕后使用fclose()关闭文件句柄
  3. 大文件处理:写入大文件时,分块写入避免内存溢出
  4. 并发控制:多进程环境使用flock()进行文件锁定
  5. 缓冲优化:频繁小写入时,考虑使用输出缓冲或自定义缓冲区
  6. 二进制文件:处理二进制文件时使用二进制模式(如'wb')
  7. 文件锁定:在写入前锁定文件,写入后释放锁
  8. 性能监控:对于大量写入操作,监控写入速度和资源使用

相关函数

函数 描述 与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. 考虑使用流式处理