PHP Conference Japan 2024

fwrite

(PHP 4, PHP 5, PHP 7, PHP 8)

fwrite二进制安全的文件写入

描述

fwrite(资源 $stream, 字符串 $data, ?整数 $length = null): 整数|false

fwrite()data 的内容写入由 stream 指向的文件流。

参数

stream

一个文件系统指针 资源,通常使用 fopen() 创建。

data

要写入的字符串。

length

如果 length 是一个 整数,则写入将在写入 length 字节或到达 data 的末尾后停止,以先发生者为准。

返回值

fwrite() 返回写入的字节数,或者在失败时返回 false

错误/异常

fwrite() 在失败时引发 E_WARNING

变更日志

版本 描述
8.0.0 length 现在可以为 null。

示例

示例 #1 一个简单的 fwrite() 示例

<?php
$filename
= 'test.txt';
$somecontent = "将此添加到文件中\n";

// 首先确保文件存在且可写。
if (is_writable($filename)) {

// 在我们的示例中,我们以追加模式打开 $filename。
// 文件指针位于文件的底部,因此
// 当我们 fwrite() 它时,$somecontent 将出现在那里。
if (!$fp = fopen($filename, 'a')) {
echo
"无法打开文件 ($filename)";
exit;
}

// 将 $somecontent 写入我们打开的文件。
if (fwrite($fp, $somecontent) === FALSE) {
echo
"无法写入文件 ($filename)";
exit;
}

echo
"成功,将 ($somecontent) 写入文件 ($filename)";

fclose($fp);

} else {
echo
"文件 $filename 不可写";
}
?>

注释

注意:

写入网络流可能在整个字符串写入之前结束。可以检查 fwrite() 的返回值

<?php
function fwrite_stream($fp, $string) {
for (
$written = 0; $written < strlen($string); $written += $fwrite) {
$fwrite = fwrite($fp, substr($string, $written));
if (
$fwrite === false) {
return
$written;
}
}
return
$written;
}
?>

注意:

在区分二进制文件和文本文件的系统(例如 Windows)上,必须在 fopen() 模式参数中包含 'b' 来打开文件。

注意:

如果 stream 以追加模式使用 fopen() 打开,则 fwrite() 是原子的(除非 data 的大小超过文件系统的块大小,在某些平台上,并且只要文件位于本地文件系统上)。也就是说,在调用 fwrite() 之前不需要 flock() 资源;所有数据都将在没有中断的情况下写入。

注意:

如果两次写入文件指针,则数据将追加到文件内容的末尾

<?php
$fp
= fopen('data.txt', 'w');
fwrite($fp, '1');
fwrite($fp, '23');
fclose($fp);

// 'data.txt' 的内容现在是 123 而不是 23!
?>

参见

添加注释

用户贡献的注释 10 个注释

nate at frickenate dot com
14 年前
在遇到 fwrite() 在预期返回 false 的情况下返回 0 的问题后,我查看了 php 的 fwrite() 本身的源代码。该函数只有在你传入无效参数时才会返回 false。任何其他错误,例如管道损坏或连接关闭,都会导致返回值小于 strlen($string),在大多数情况下为 0。

因此,使用重复调用 fwrite() 直到写入的字节数之和等于完整值的 strlen() 或者在出错时期望 false 的循环,如果连接丢失,将导致无限循环。

这意味着文档中的 fwrite_stream() 代码示例以及评论中其他人发布的所有“辅助”函数都已损坏。您*必须*检查返回值是否为 0,并立即中止或跟踪最大重试次数。

下面是文档中的示例。此代码很糟糕,因为管道损坏会导致 fwrite() 无限循环并返回 0。由于循环仅在 fwrite() 返回 false 或成功写入所有字节时才会中断,因此在失败时会发生无限循环。

<?php
// 存在问题的函数 - 当 fwrite() 返回 0 时会陷入无限循环
function fwrite_stream($fp, $string) {
for (
$written = 0; $written < strlen($string); $written += $fwrite) {
$fwrite = fwrite($fp, substr($string, $written));
if (
$fwrite === false) {
return
$written;
}
}
return
$written;
}
?>
divinity76 at gmail dot com
3年前
如果你需要一个写入所有数据的函数,可以尝试

<?php

/**
* 写入所有数据或抛出异常
*
* @param mixed $handle
* @param string $data
* @throws \RuntimeException 当 fwrite 返回 <1 但仍有更多数据需要写入时
* @return void
*/
/*private static*/
function fwrite_all($handle, string $data): void
{
$original_len = strlen($data);
if (
$original_len > 0) {
$len = $original_len;
$written_total = 0;
for (;;) {
$written_now = fwrite($handle, $data);
if (
$written_now === $len) {
return;
}
if (
$written_now < 1) {
throw new
\RuntimeException("只能写入 {$written_total}/{$original_len} 字节!");
}
$written_total += $written_now;
$data = substr($data, $written_now);
$len -= $written_now;
// assert($len > 0);
// assert($len === strlen($data));
}
}
}
niklesh at example dot com
4年前
$handles 也可以用于像下面示例一样输出到控制台

fwrite(STDOUT, "控制台输出");
Chris Blown
21年前
不要忘记检查 fwrite 的返回值以判断是否存在错误!仅仅因为你成功地打开了文件用于写入,并不总是意味着你可以写入它。

在某些系统上,如果文件系统已满,可能会发生这种情况,你仍然可以打开文件并创建文件系统 inode,但 fwrite 会失败,导致生成一个零字节的文件。
匿名
8年前
// 你是想复制虚拟文件还是发送虚拟文件
// 可以发送大于 4GB 的文件并在不使用 FSEEK 的情况下写入,受 PHP_INT_MAX 限制。它适用于 32 位或 64 位系统
// fwrite 和 fread 没有指针位置限制

<?php

$gfz
= filesize_dir("d:\\starwars.mkv"); // 11.5GB
echo 'Z:',$gfz,PHP_EOL;

$fz = fopen('d:\\test2.mkv', 'wb');
$fp = fopen('d:\\starwars.mkv', 'rb');
echo
PHP_EOL;
$a = (float) 0;
while((
$l=fread($fp, 65536))) {
fwrite($fz, $l);
if((
$a+=65536)%5) echo "\r", '>', $a, ' : ' , $gfz;
}

fclose($fp);
fclose($fz);

// test2.mkv' 是 11.5GB

function filesize_dir($file) {
exec('dir ' . $file, $inf);
$size_raw = $inf[6];
$size_exp = explode(" ",$size_raw);
$size_ext = $size_exp[19];
$size_int = (float) str_replace(chr(255), '', $size_ext);
return
$size_int;
}
?>
dharris dot nospam at removethispart dot drh dot net
16年前
有些人说,当写入套接字时,可能并非所有请求写入的字节都被写入。你可能需要再次调用 fwrite 来写入第一次未写入的字节。(至少 UNIX 中的 write() 系统调用就是这样工作的。)

这是一个有帮助的代码(警告:未在多字节字符集中进行测试)

function fwrite_with_retry($sock, &$data)
{
$bytes_to_write = strlen($data);
$bytes_written = 0;

while ( $bytes_written < $bytes_to_write )
{
if ( $bytes_written == 0 ) {
$rv = fwrite($sock, $data);
} else {
$rv = fwrite($sock, substr($data, $bytes_written));
}

if ( $rv === false || $rv == 0 )
return( $bytes_written == 0 ? false : $bytes_written );

$bytes_written += $rv;
}

return $bytes_written;
}

像这样调用它

$rv = fwrite_with_retry($sock, $request_string);

if ( ! $rv )
die("无法将 request_string 写入套接字");
if ( $rv != strlen($request_string) )
die("在写入 request_string 时套接字写入排序");
synnus at gmail dot com
8年前
// 你是想复制虚拟文件还是发送虚拟文件
// 可以发送大于 4GB 的文件并在不使用 FSEEK 的情况下写入,受 PHP_INT_MAX 限制。它适用于 32 位或 64 位系统
// fwrite 和 fread 没有指针位置限制

<?php

$gfz
= filesize_dir("d:\\starwars.mkv"); // 11.5GB
echo 'Z:',$gfz,PHP_EOL;

$fz = fopen('d:\\test2.mkv', 'wb');
$fp = fopen('d:\\starwars.mkv', 'rb');
echo
PHP_EOL;
$a = (float) 0;
while((
$l=fread($fp, 65536))) {
fwrite($fz, $l);
if((
$a+=65536)%5) echo "\r", '>', $a, ' : ' , $gfz;
}

fclose($fp);
fclose($fz);

// test2.mkv' 是 11.5GB

function filesize_dir($file) {
exec('dir ' . $file, $inf);
$size_raw = $inf[6];
$size_exp = explode(" ",$size_raw);
$size_ext = $size_exp[19];
$size_int = (float) str_replace(chr(255), '', $size_ext);
return
$size_int;
}
?>
php at biggerthanthebeatles dot com
21年前
希望这对其他新手有所帮助。

如果你正在将数据写入 Windows 系统上的 txt 文件并且需要换行符。使用 \r\n 。这将写入十六进制 OD OA。

例如:
$batch_data= "一些数据... \r\n";
fwrite($fbatch,$batch_data);

这相当于在记事本中打开一个 txt 文件,在行尾按下 Enter 并保存它。
匿名
15年前
如果你在文件中间使用指针写入,它会覆盖那里的内容,而不是将文件的其余部分移位。
chedong at hotmail dot com
21年前
如果没有给出长度参数,fwrite 输出会去除斜杠,例如

<?php
$str
= "c:\\01.txt";
$out = fopen("out.txt", "w");
fwrite($out, $str);
fclose($out);
?>

out.txt 将会是
c:^@1.txt
没有转义的 '\\0' 将是 '\0' ==> 0x00。

正确的做法是将 fwrite 更改为
fwrite($out, $str, strlen($str));
To Top