注意 - 使用'w'模式下的fopen不会像您预期的那样更新文件的修改时间(filemtime)。您可能希望在写入和关闭文件后发出touch(),这将更新其修改时间。如果您想保持您的头发,这在缓存情况下可能变得至关重要。
(PHP 4, PHP 5, PHP 7, PHP 8)
fopen — 打开文件或 URL
fopen() 将由 filename
指定的命名资源绑定到一个流。
filename
如果 filename
的格式为 "scheme://...",则假定它是一个 URL,PHP 将为此方案搜索协议处理程序(也称为包装器)。如果未注册该协议的包装器,PHP 将发出通知以帮助您跟踪脚本中的潜在问题,然后继续,就像 filename
指定了一个常规文件一样。
如果 PHP 确定 filename
指定了一个本地文件,则它将尝试在此文件上打开一个流。PHP 必须能够访问该文件,因此您需要确保文件访问权限允许此访问。如果您启用了 open_basedir,则可能适用进一步的限制。
如果 PHP 确定 filename
指定了一个已注册的协议,并且该协议注册为网络 URL,则 PHP 将检查是否启用了 allow_url_fopen。如果将其关闭,PHP 将发出警告,并且 fopen() 调用将失败。
注意:
支持的协议列表可以在 支持的协议和包装器 中找到。某些协议(也称为
wrappers
)支持context
和/或 php.ini 选项。有关可设置的选项列表,请参阅正在使用的协议的特定页面。(例如,php.ini 值user_agent
由http
包装器使用)。
在 Windows 平台上,请注意转义路径中使用的任何反斜杠,或使用正斜杠。
<?php
$handle = fopen("c:\\folder\\resource.txt", "r");
?>
mode
mode
参数指定您需要对流的访问类型。它可以是以下任何一个
mode |
描述 |
---|---|
'r' |
只读打开;将文件指针置于文件开头。 |
'r+' |
读写打开;将文件指针置于文件开头。 |
'w' |
只写打开;将文件指针置于文件开头并将文件截断为零长度。如果文件不存在,则尝试创建它。 |
'w+' |
读写打开;否则与 'w' 的行为相同。 |
'a' |
只写打开;将文件指针置于文件末尾。如果文件不存在,则尝试创建它。在此模式下,fseek() 无效,写入始终追加。 |
'a+' |
读写打开;将文件指针置于文件末尾。如果文件不存在,则尝试创建它。在此模式下,fseek() 仅影响读取位置,写入始终追加。 |
'x' |
创建并只写打开;将文件指针置于文件开头。如果文件已存在,则 fopen() 调用将失败,返回 false 并生成级别为 E_WARNING 的错误。如果文件不存在,则尝试创建它。这等效于为底层的 open(2) 系统调用指定 O_EXCL|O_CREAT 标志。 |
'x+' |
创建并读写打开;否则与 'x' 的行为相同。 |
'c' |
只写打开文件。如果文件不存在,则创建它。如果它存在,则不会截断(与 'w' 相反),也不会导致此函数调用失败(与 'x' 的情况一样)。文件指针位于文件开头。如果需要在尝试修改文件之前获取建议锁(参见 flock()),这可能很有用,因为使用 'w' 可能会在获取锁之前截断文件(如果需要截断,则可以在请求锁后使用 ftruncate())。 |
'c+' |
读写打开文件;否则与 'c' 的行为相同。 |
'e' |
在打开的文件描述符上设置 close-on-exec 标志。仅在 POSIX.1-2008 符合系统的 PHP 编译版本中可用。 |
注意:
不同的操作系统系列具有不同的换行约定。当您写入文本文件并想要插入换行符时,您需要使用操作系统正确的换行符。基于 Unix 的系统使用
\n
作为换行符,基于 Windows 的系统使用\r\n
作为换行符,而基于 Macintosh 的系统(Mac OS Classic)使用\r
作为换行符。如果您在写入文件时使用了错误的换行符,您可能会发现打开这些文件的其他应用程序会“看起来很奇怪”。
Windows 提供了一个文本模式转换标志(
't'
),它会在处理文件时将\n
透明地转换为\r\n
。相反,您也可以使用'b'
强制使用二进制模式,这不会转换您的数据。要使用这些标志,请将'b'
或't'
指定为mode
参数的最后一个字符。默认转换模式为
'b'
。如果您正在处理纯文本文件并且使用\n
在脚本中分隔换行符,但希望您的文件可以使用旧版本的记事本等应用程序读取,则可以使用't'
模式。在所有其他情况下,您应该使用'b'
。如果您在处理二进制文件时指定了 't' 标志,则可能会遇到数据方面的一些奇怪问题,包括损坏的图像文件以及
\r\n
字符的奇怪问题。
注意:
为了可移植性,还强烈建议您重写使用或依赖
't'
模式的代码,以便它改用正确的换行符和'b'
模式。
注意:
mode
将忽略 php://output、php://input、php://stdin、php://stdout、php://stderr 和 php://fd 流包装器。
use_include_path
如果要也在 include_path 中搜索文件,则可选的第三个 use_include_path
参数可以设置为 true
。
context
成功时返回文件指针资源,失败时返回 false
失败时,将发出 E_WARNING
。
版本 | 描述 |
---|---|
7.0.16, 7.1.2 | 添加了 'e' 选项。 |
示例 #1 fopen() 示例
<?php
$handle = fopen("/home/rasmus/file.txt", "r");
$handle = fopen("/home/rasmus/file.gif", "wb");
$handle = fopen("http://www.example.com/", "r");
$handle = fopen("ftp://user:[email protected]/somefile.txt", "w");
?>
使用SSL时,Microsoft IIS会违反协议,在未发送close_notify
指示符的情况下关闭连接。当您到达数据末尾时,PHP会将其报告为“SSL:致命协议错误”。为解决此问题,应将error_reporting的值降低到不包含警告的级别。当您使用https://
包装器打开流时,PHP可以检测到有问题的IIS服务器软件,并将抑制警告。当使用fsockopen()创建ssl://
套接字时,开发人员负责检测和抑制此警告。
注意:
如果您在读取和写入文件时遇到问题,并且正在使用PHP的服务器模块版本,请记住确保您正在使用的文件和目录对服务器进程是可访问的。
注意:
当
filename
是目录时,此函数也可能成功。如果您不确定filename
是文件还是目录,则可能需要在调用fopen()之前使用is_dir()函数。
注意 - 使用'w'模式下的fopen不会像您预期的那样更新文件的修改时间(filemtime)。您可能希望在写入和关闭文件后发出touch(),这将更新其修改时间。如果您想保持您的头发,这在缓存情况下可能变得至关重要。
有一种未公开的模式可以使fopen非阻塞(在Windows上不起作用)。通过在模式参数中添加'n',fopen将不会阻塞,但是如果管道不存在,则会引发错误。
$fp = fopen("/tmp/debug", "a"); //如果管道不存在则阻塞
$fp = fopen("/tmp/debug", "an"); //管道不存在则引发错误
在Apache 2.2.4上的php 5.2.5中,使用fopen()或readfile()访问ftp服务器上的文件需要额外的正斜杠,如果需要绝对路径。
即,如果名为bullbes.txt的文件存储在ftp服务器example.com上的/var/school/下,并且您正在尝试使用用户blossom和密码buttercup访问它,则url将为
ftp://blossom:[email protected]//var/school/bubbles.txt
注意两个正斜杠。第二个斜杠似乎是必要的,这样服务器就不会将路径解释为townsville上blossom的主目录的相对路径。
阅读文字描述以了解fopen模式的预期结果需要一些时间。此csv表格可以帮助更快地理解以找到您正在寻找的模式
模式,创建,读取,写入,指针起始位置,截断文件,注释,用途
r,,,y,,开头,,如果文件不存在则失败,基本读取现有文件
r+,,,y,y,开头,,如果文件不存在则失败,基本读写现有文件
w,y,,y,开头+结尾,y,,"创建,擦除,写入文件"
w+,y,y,y,开头+结尾,y,,"创建,擦除,写入文件并具有读取选项"
a,y,,y,结尾,,,"从文件结尾写入,如果需要则创建"
a+,y,y,y,结尾,,,"从文件结尾写入,如果需要则创建,并具有读取选项"
x,y,,y,开头,,如果文件存在则失败,"类似于w,但防止覆盖现有文件"
x+,y,y,y,开头,,如果文件存在则失败,"类似于w+,但防止覆盖现有文件"
c,y,,y,开头,,,打开/创建文件以写入而不删除当前内容
c+,y,y,y,开头,,,"打开/创建文件,先读取,然后写入"
请注意,您是否可以打开目录取决于操作系统。以下几行
<?php
// Windows ($fh === false)
$fh = fopen('c:\\Temp', 'r');
// UNIX (is_resource($fh) === true)
$fh = fopen('/tmp', 'r');
?>
表明在Windows(2000,可能是XP)上,您可能无法打开目录(错误是“权限被拒绝”),无论该目录的安全权限如何。
在UNIX上,您可以愉快地读取原生文件系统的目录格式。
打开包含多字节数据的文件(例如:données multi-octets)时,遇到一些编码问题。了解到它使用windows-1250。使用iconv将其转换为UTF-8,解决了这个问题。
<?php
function utf8_fopen_read($fileName) {
$fc = iconv('windows-1250', 'utf-8', file_get_contents($fileName));
$handle=fopen("php://memory", "rw");
fwrite($handle, $fc);
fseek($handle, 0);
return $handle;
}
?>
示例用法
<?php
$fh = utf8_fopen_read("./tpKpiBundle.csv");
while (($data = fgetcsv($fh, 1000, ",")) !== false) {
foreach ($data as $value) {
echo $value . "<br />\n";
}
}
?>
希望对您有所帮助。
当我将服务器迁移到新的Fedora 4安装时,我无法让某个php脚本工作。问题是,当尝试通过apache访问文件作为URL时,fopen()失败了——即使它在从shell运行时工作正常,并且即使该文件可以从任何浏览器轻松读取。在试图将责任归咎于Apache、RedHat,甚至我的猫和狗之后,我最终在RedHat的网站上发现了这个错误报告
https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=164700
问题根本在于 SELinux(我之前对此一无所知)——你需要运行以下命令才能让 SELinux 允许 php 打开网页文件
/usr/sbin/setsebool httpd_can_network_connect=1
要使更改永久生效,请使用 -P 选项运行它
/usr/sbin/setsebool -P httpd_can_network_connect=1
希望这对其他人有所帮助——我花了很长时间才找到这个问题。
如果要打开的文件是命名管道 (FIFO),则 fopen() 将阻塞。无论以 "r" 还是 "w" 模式打开都是如此。(参见 man 7 fifo:这是正确、默认的行为;尽管 Linux 支持对命名管道的非阻塞 fopen(),但 PHP 不支持)。
这样做的结果是,你无法发现初始的命名管道读/写是否会阻塞,因为要做到这一点,你需要 stream_select(),而这反过来又需要 fopen() 已经执行!
此函数在创建文件夹之前检查递归权限和父文件夹的递归存在性。以避免生成错误/警告。
/**
* 此函数检查递归权限和父文件夹的递归存在性,
* 在创建文件夹之前。以避免生成错误/警告。
*
* @return bool
* true 文件夹已创建或存在且可写。
* false 文件夹不存在且无法创建。
*/
function createWritableFolder($folder)
{
if (file_exists($folder)) {
// 文件夹存在。
return is_writable($folder);
}
// 文件夹不存在,检查父文件夹。
$folderParent = dirname($folder);
if($folderParent != '.' && $folderParent != '/' ) {
if(!createWritableFolder(dirname($folder))) {
// 创建父文件夹失败。
return false;
}
// 父文件夹已创建。
}
if ( is_writable($folderParent) ) {
// 父文件夹可写。
if ( mkdir($folder, 0777, true) ) {
// 文件夹已创建。
return true;
}
// 创建文件夹失败。
}
// 父文件夹不可写。
return false;
}
/**
* 此函数检查递归权限和父文件夹的递归存在性,
* 在创建文件/文件夹之前。以避免生成错误/警告。
*
* @return bool
* true 已创建或文件存在且可写。
* false 文件不存在且无法创建。
*/
function createWritableFile($file)
{
// 检查配置文件是否存在。
if (file_exists($file)) {
// 检查配置文件是否可写。
return is_writable($file);
}
// 检查配置文件是否存在并尝试创建配置文件。
if(createWritableFolder(dirname($file)) && ($handle = fopen($file, 'a'))) {
fclose($handle);
return true; // 配置文件已创建。
}
// 配置文件不可访问。
return false;
}
<?php
#由于
#因文件被锁定而中止写入是不正确的,因此将更新上次用户计数器脚本。
$counter_file = '/tmp/counter.txt';
clearstatcache();
ignore_user_abort(true); ## 防止刷新中止文件操作和损坏文件
if (file_exists($counter_file)) {
$fh = fopen($counter_file, 'r+');
while(1) {
if (flock($fh, LOCK_EX)) {
#$buffer = chop(fgets($fh, 2));
$buffer = chop(fread($fh, filesize($counter_file)));
$buffer++;
rewind($fh);
fwrite($fh, $buffer);
fflush($fh);
ftruncate($fh, ftell($fh));
flock($fh, LOCK_UN);
break;
}
}
}
else {
$fh = fopen($counter_file, 'w+');
fwrite($fh, "1");
$buffer="1";
}
fclose($fh);
print "Count is $buffer";
?>
一个简单的类,用于获取 HTTP URL。支持“Location:”重定向。对于 allow_url_fopen=false 的服务器很有用。适用于使用 SSL 保护的主机。
<?php
# 用法:
$r = new HTTPRequest('http://www.example.com');
echo $r->DownloadToString();
class HTTPRequest
{
var $_fp; // HTTP 套接字
var $_url; // 全URL
var $_host; // HTTP 主机
var $_protocol; // 协议 (HTTP/HTTPS)
var $_uri; // 请求URI
var $_port; // 端口
// 解析URL
function _scan_url()
{
$req = $this->_url;
$pos = strpos($req, '://');
$this->_protocol = strtolower(substr($req, 0, $pos));
$req = substr($req, $pos+3);
$pos = strpos($req, '/');
if($pos === false)
$pos = strlen($req);
$host = substr($req, 0, $pos);
if(strpos($host, ':') !== false)
{
list($this->_host, $this->_port) = explode(':', $host);
}
else
{
$this->_host = $host;
$this->_port = ($this->_protocol == 'https') ? 443 : 80;
}
$this->_uri = substr($req, $pos);
if($this->_uri == '')
$this->_uri = '/';
}
// 构造函数
function HTTPRequest($url)
{
$this->_url = $url;
$this->_scan_url();
}
// 下载URL到字符串
function DownloadToString()
{
$crlf = "\r\n";
// 生成请求
$req = 'GET ' . $this->_uri . ' HTTP/1.0' . $crlf
. 'Host: ' . $this->_host . $crlf
. $crlf;
// 获取
$this->_fp = fsockopen(($this->_protocol == 'https' ? 'ssl://' : '') . $this->_host, $this->_port);
fwrite($this->_fp, $req);
while(is_resource($this->_fp) && $this->_fp && !feof($this->_fp))
$response .= fread($this->_fp, 1024);
fclose($this->_fp);
// 分离头部和主体
$pos = strpos($response, $crlf . $crlf);
if($pos === false)
return($response);
$header = substr($response, 0, $pos);
$body = substr($response, $pos + 2 * strlen($crlf));
// 解析头部
$headers = array();
$lines = explode($crlf, $header);
foreach($lines as $line)
if(($pos = strpos($line, ':')) !== false)
$headers[strtolower(trim(substr($line, 0, $pos)))] = trim(substr($line, $pos+1));
// 重定向?
if(isset($headers['location']))
{
$http = new HTTPRequest($headers['location']);
return($http->DownloadToString($http));
}
else
{
return($body);
}
}
}
?>
如果提供没有文件名的路径,PHP 将打开一个目录。我刚刚被这个问题困扰了。我没有检查连接字符串的文件名部分。
例如
<?php
$fd = fopen('/home/mydir/' . $somefile, 'r');
?>
如果$somefile = '',则将打开目录。
如果尝试使用文件句柄读取,您将获得二进制目录内容。我尝试了追加模式,它会出错,所以似乎并不危险。
这是在 FreeBSD 4.5 和 PHP 4.3.1 上的测试结果。在 4.1.1 和 PHP 4.1.2 上的行为相同。我没有测试其他版本/操作系统组合。
"不要使用以下保留的设备名称作为文件名
CON、PRN、AUX、NUL、COM1、COM2、COM3、COM4、COM5、COM6、COM7、COM8、COM9、LPT1、
LPT2、LPT3、LPT4、LPT5、LPT6、LPT7、LPT8 和 LPT9。也请避免使用这些名称
后跟扩展名;例如,不建议使用 NUL.txt。
更多信息,请参见命名空间"
这是一个 Windows 限制。
参见
http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>文档</title>
</head>
<body>
<?php
// generiereHostliste.php
function generiereHostliste($file) {
// 从 Rechnerliste.csv 读取
$fp = fopen($file, "r");
while($row = fgetcsv($fp, 0, ";")) {
$liste[]=[$row[0].";10.16.".$row[1].".".$row[2]];
}
fclose($fp);
// 写入 Hostliste.csv
$fp = fopen("Hostliste.csv", "w");
foreach($liste as $row) {
echo "<pre>";
print_r($row);
echo "</pre>";
fputcsv($fp, $row, ";");
}
fclose($fp);
}
// 测试
$file = "Rechnerliste.csv";
generiereHostliste($file);
?>
</body>
</html>
下载:我需要一个函数来模拟“wget url”,并且不将数据缓冲到内存中,以避免大型文件出现此类问题
<?php
function download($file_source, $file_target) {
$rh = fopen($file_source, 'rb');
$wh = fopen($file_target, 'wb');
if ($rh===false || $wh===false) {
// 读取或打开文件出错
return true;
}
while (!feof($rh)) {
if (fwrite($wh, fread($rh, 1024)) === FALSE) {
// '下载错误:无法写入文件('.$file_target.')';
return true;
}
}
fclose($rh);
fclose($wh);
// 无错误
return false;
}
?>
我正在为 Win32 开发一个控制台脚本,并注意到一些情况。在 Win32 上,似乎无法重新打开输入流进行读取,而必须只打开一次,然后从中读取。此外,我不知道这是错误还是什么,但似乎 fgets() 总是读取到换行符为止。返回的字符数是正确的,但它不会停止读取并返回到脚本。我现在不知道解决方法,但我将继续努力。
这是一些用于解决 stdin 关闭和重新打开问题的代码。
<?php
function read($length='255'){
if (!isset($GLOBALS['StdinPointer'])){
$GLOBALS['StdinPointer']=fopen("php://stdin","r");
}
$line=fgets($GLOBALS['StdinPointer'],$length);
return trim($line);
}
echo "请输入您的姓名: ";
$name=read();
echo "请输入您的年龄: ";
$age=read();
echo "您好,$name,$age岁真棒!" ;
@fclose($StdinPointer);
?>
如果您需要对 URL 使用 fopen() 来设置超时,您可以这样做
<?php
$timeout = 3;
$old = ini_set('default_socket_timeout', $timeout);
$file = fopen('http://example.com', 'r');
ini_set('default_socket_timeout', $old);
stream_set_timeout($file, $timeout);
stream_set_blocking($file, 0);
// 其余部分是标准的
?>
请注意,您无法写入 HTTP 资源,例如执行 PUT 请求。
您将收到此错误:'Failed to open stream: HTTP wrapper does not support writeable connections'
要执行 PUT 操作,您只能填充 HTTP 上下文的“content”键,或者使用 Curl。
以“r+”模式打开文件,然后尝试在读取文件之前使用 ftruncate 设置文件指针位置将导致文件数据丢失,就像您以“w”模式打开文件一样。
例如
$File = fopen($FilePath,"r+"); // 以读写模式打开文件
ftruncate($File, 0); // 设置指针位置(将擦除数据)
while(! feof($File)) { // 继续直到到达文件结尾
$Line = fgets($File); // 将文件的一行读取到字符串中
$Line = trim($Line); // 修剪字符串的换行符
}
ftruncate($File,0); // (将不会擦除数据)
fclose($File);