PHP fputs()函数

fputs()函数fwrite()函数的别名,两者功能完全相同。用于向打开的文件写入数据。
重要说明:fputs()是fwrite()的历史遗留别名,建议在新代码中使用fwrite()

语法

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

参数说明

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

返回值

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

fputs() 与 fwrite() 的关系

特性 fputs() fwrite()
函数本质 fwrite()的别名 原始函数
函数签名 完全相同 完全相同
性能 完全相同 完全相同
推荐使用 不推荐,历史遗留 推荐,标准名称
代码可读性 较差,可能引起混淆 较好,语义明确

示例代码

示例1:基本文件写入

<?php
// 使用fputs()写入文件
$filename = 'test.txt';
$handle = fopen($filename, 'w');

if ($handle === false) {
    die('无法打开文件');
}

// 写入字符串
$string = "Hello, World!\n";
$bytes = fputs($handle, $string);

if ($bytes === false) {
    echo '写入失败';
} else {
    echo "写入成功,写入字节数: $bytes<br>";
}

// 写入多行数据
fputs($handle, "这是第二行文本\n");
fputs($handle, "这是第三行文本\n");

// 关闭文件
fclose($handle);

echo "文件内容:<br>";
echo nl2br(htmlspecialchars(file_get_contents($filename)));
?>

示例2:使用length参数限制写入长度

<?php
$filename = 'limited.txt';
$handle = fopen($filename, 'w');

if ($handle) {
    $text = "这是一个非常长的字符串,但是我们将只写入前一部分。";

    // 只写入前10个字节
    $bytes = fputs($handle, $text, 10);
    echo "写入字节数: $bytes<br>";

    // 关闭文件
    fclose($handle);

    // 显示写入的内容
    $content = file_get_contents($filename);
    echo "实际写入内容: " . htmlspecialchars($content) . "<br>";
    echo "字符串长度: " . strlen($content) . " 字节<br>";
}

// 另一个例子:分块写入
$handle = fopen('chunks.txt', 'w');
if ($handle) {
    $data = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

    // 每次写入5个字符
    for ($i = 0; $i < strlen($data); $i += 5) {
        $chunk = substr($data, $i, 5);
        fputs($handle, $chunk, 5);
    }

    fclose($handle);

    echo "<br>分块写入完成,文件内容: " . file_get_contents('chunks.txt');
}
?>

示例3:日志文件记录器

<?php
class FileLogger {
    private $handle;
    private $filename;

    public function __construct($filename) {
        $this->filename = $filename;
        // 以追加模式打开文件(如果文件不存在则创建)
        $this->handle = fopen($filename, 'a');

        if (!$this->handle) {
            throw new Exception("无法打开日志文件: $filename");
        }
    }

    /**
     * 记录日志
     */
    public function log($message, $level = 'INFO') {
        $timestamp = date('Y-m-d H:i:s');
        $logEntry = "[$timestamp] [$level] $message\n";

        // 使用fputs写入日志
        $bytes = fputs($this->handle, $logEntry);

        if ($bytes === false) {
            throw new Exception("日志写入失败");
        }

        return $bytes;
    }

    /**
     * 批量记录日志
     */
    public function logBatch(array $messages, $level = 'INFO') {
        $batch = '';
        $timestamp = date('Y-m-d H:i:s');

        foreach ($messages as $message) {
            $batch .= "[$timestamp] [$level] $message\n";
        }

        return fputs($this->handle, $batch);
    }

    /**
     * 获取日志文件大小
     */
    public function getFileSize() {
        return filesize($this->filename);
    }

    /**
     * 清空日志文件
     */
    public function clear() {
        fclose($this->handle);
        $this->handle = fopen($this->filename, 'w');
        return $this->handle !== false;
    }

    public function __destruct() {
        if ($this->handle) {
            fclose($this->handle);
        }
    }
}

// 使用示例
try {
    $logger = new FileLogger('app.log');

    // 记录单条日志
    $logger->log('应用程序启动');
    $logger->log('用户登录成功', 'INFO');
    $logger->log('数据库连接失败', 'ERROR');
    $logger->log('用户执行了重要操作', 'WARNING');

    // 批量记录
    $messages = [
        '批量日志条目1',
        '批量日志条目2',
        '批量日志条目3'
    ];
    $logger->logBatch($messages);

    echo "日志记录完成<br>";
    echo "日志文件大小: " . $logger->getFileSize() . " 字节<br><br>";

    // 显示日志内容
    echo "<h5>日志文件内容:</h5>";
    echo "<pre style='background:#f8f9fa;padding:10px;border-radius:4px;max-height:200px;overflow:auto;'>";
    echo htmlspecialchars(file_get_contents('app.log'));
    echo "</pre>";

} catch (Exception $e) {
    echo "错误: " . $e->getMessage();
}
?>

示例4:大文件生成器(性能对比)

<?php
// 生成大文件的性能对比:fputs() vs file_put_contents()

function generateLargeFile_fputs($filename, $sizeInMB) {
    $start = microtime(true);
    $handle = fopen($filename, 'w');

    if (!$handle) {
        return false;
    }

    $chunkSize = 1024 * 1024; // 1MB
    $data = str_repeat('A', $chunkSize);
    $iterations = $sizeInMB;

    for ($i = 0; $i < $iterations; $i++) {
        fputs($handle, $data);
    }

    fclose($handle);
    $time = microtime(true) - $start;

    return [
        'method' => 'fputs',
        'time' => $time,
        'size' => $sizeInMB * $chunkSize
    ];
}

function generateLargeFile_fileputcontents($filename, $sizeInMB) {
    $start = microtime(true);

    $chunkSize = 1024 * 1024; // 1MB
    $data = str_repeat('A', $chunkSize);
    $iterations = $sizeInMB;

    for ($i = 0; $i < $iterations; $i++) {
        file_put_contents($filename, $data, FILE_APPEND);
    }

    $time = microtime(true) - $start;

    return [
        'method' => 'file_put_contents',
        'time' => $time,
        'size' => $sizeInMB * $chunkSize
    ];
}

// 测试生成10MB文件
$sizeMB = 10;

echo "<h5>生成 {$sizeMB}MB 文件性能对比:</h5>";

// 使用fputs
$result1 = generateLargeFile_fputs('large_fputs.dat', $sizeMB);
if ($result1) {
    echo "fputs() 耗时: " . number_format($result1['time'], 4) . " 秒<br>";
    echo "文件大小: " . number_format($result1['size'] / (1024*1024), 2) . " MB<br><br>";
}

// 使用file_put_contents
$result2 = generateLargeFile_fileputcontents('large_fileputcontents.dat', $sizeMB);
if ($result2) {
    echo "file_put_contents() 耗时: " . number_format($result2['time'], 4) . " 秒<br>";
    echo "文件大小: " . number_format($result2['size'] / (1024*1024), 2) . " MB<br><br>";
}

// 清理测试文件
@unlink('large_fputs.dat');
@unlink('large_fileputcontents.dat');

// 实际建议:对于大文件写入,使用fwrite/fputs并分块写入
function writeLargeFileEfficiently($filename, $dataGenerator, $chunkSize = 8192) {
    $handle = fopen($filename, 'w');
    if (!$handle) return false;

    foreach ($dataGenerator() as $chunk) {
        fputs($handle, $chunk, $chunkSize);
    }

    fclose($handle);
    return true;
}

echo "<h5>高效写入大文件的模式:</h5>";
echo "使用fputs()分块写入大文件是最佳实践,可以避免内存溢出。";
?>

注意事项

重要提示:
  • 别名问题:fputs()是fwrite()的别名,建议在新代码中直接使用fwrite()以保持代码一致性
  • 二进制安全:fputs()是二进制安全的,可以安全地写入包含空字符(\0)的数据
  • 文件指针:写入后文件指针会移动到写入内容的末尾
  • 缓冲机制:默认情况下,写入操作可能是缓冲的,使用fflush()可以强制刷新缓冲区
  • 并发写入:多个进程同时写入同一文件时,需要使用flock()进行文件锁定
  • 错误处理:始终检查返回值,失败时返回false
  • 长度参数:如果指定了length参数,即使string比length长,也只会写入length个字节
  • 编码问题:写入包含多字节字符(如中文)时,确保文件以正确的编码打开
  • 性能考虑:对于大量小写入操作,考虑合并数据后一次性写入以提高性能

最佳实践

  1. 使用fwrite而非fputs:为了代码清晰和一致性,优先使用fwrite()
  2. 错误处理:始终检查fputs/fwrite的返回值
  3. 资源管理:使用完毕后使用fclose()关闭文件句柄
  4. 大文件处理:写入大文件时,分块写入避免内存溢出
  5. 并发控制:多进程环境使用flock()进行文件锁定
  6. 缓冲优化:频繁小写入时,考虑使用输出缓冲

相关函数

函数 描述 与fputs()的关系
fwrite() 写入文件(二进制安全) fputs()是fwrite()的别名
fopen() 打开文件或URL 创建fputs()所需的文件句柄
fclose() 关闭打开的文件指针 关闭fputs()使用的文件句柄
file_put_contents() 将字符串写入文件 更简单的文件写入函数,内部使用fwrite
fflush() 将缓冲内容输出到文件 强制刷新fputs()的缓冲区
ftell() 返回文件指针位置 获取fputs()写入后的指针位置

从fputs()迁移到fwrite()

如果您有使用fputs()的旧代码,建议迁移到fwrite():

<?php
// 旧代码(使用fputs)
$handle = fopen('file.txt', 'w');
fputs($handle, 'Hello World');
fclose($handle);

// 新代码(使用fwrite)- 只需重命名函数
$handle = fopen('file.txt', 'w');
fwrite($handle, 'Hello World');  // 仅函数名不同
fclose($handle);

// 复杂的旧代码迁移示例
function oldCode($filename, $data) {
    $fp = fopen($filename, 'a');
    if ($fp) {
        fputs($fp, $data);
        fclose($fp);
        return true;
    }
    return false;
}

function newCode($filename, $data) {
    $fp = fopen($filename, 'a');
    if ($fp) {
        fwrite($fp, $data);  // 仅此更改
        fclose($fp);
        return true;
    }
    return false;
}
?>