注意 - 在 '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() 调用将失败。
注意:
支持的协议列表可以在 支持的协议和包装器 中找到。一些协议(也称为
包装器
)支持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' |
在打开的文件描述符上设置关闭执行标志。仅在 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: Fatal Protocol Error”。为了解决这个问题,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
请注意两个斜杠。看起来第二个斜杠是必需的,这样服务器就不会将路径解释为相对于 blossom 在 townsville 的主目录。
对 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 上,您可以愉快地读取本机文件系统的目录格式。
当我将服务器迁移到新的 Fedora 4 安装时,我无法让某个 php 脚本正常运行。问题是 fopen() 在尝试通过 apache 访问文件作为 URL 时失败了 - 即使在从 shell 运行时它工作正常,即使该文件可以通过任何浏览器轻松读取。在试图将责任归咎于 Apache、RedHat,甚至我的猫和狗之后,我终于在 Redhat 的网站上看到了这个错误报告
https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=164700
基本上问题是 SELinux(我对此一无所知) - 您必须运行以下命令才能让 SELinux 允许 php 打开 web 文件
/usr/sbin/setsebool httpd_can_network_connect=1
要使更改永久生效,请使用 -P 选项运行它
/usr/sbin/setsebool -P httpd_can_network_connect=1
希望这能帮助其他人 - 我花了很长时间才找到这个问题。
此函数在创建文件夹之前检查递归权限和父文件夹的递归存在。为了避免产生错误/警告。
/**
* 此函数检查递归权限和父文件夹的递归存在。
* 在创建文件夹之前。为了避免产生错误/警告。
*
* @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;
}
如果要打开的文件是 fifo,则 fopen() 会阻塞。这在以 "r" 或 "w" 模式打开时都是如此。(参见 man 7 fifo:这是正确的默认行为;虽然 Linux 支持 fifo 的非阻塞 fopen(),但 PHP 不支持)。
这会导致您无法发现初始 fifo 读取/写入是否会阻塞,因为要做到这一点,您需要 stream_select(),而 stream_select() 又需要 fopen() 已完成!
在使用多字节数据(例如:données multi-octets)打开文件时,遇到了编码问题。得知它使用的是 windows-1250。使用 iconv 将其转换为 UTF-8,解决了问题。
<?php
函数 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);
返回 $handle;
}
?>
示例用法
<?php
$fh = utf8_fopen_read("./tpKpiBundle.csv");
当 (($data = fgetcsv($fh, 1000, ",")) 不等于 false) {
遍历 $data 作为 $value {
回显 $value . "<br />\n";
}
}
?>
希望有帮助。
<?php
# 由于文件被锁而中止写入是不正确的,所以要更新最后一个用户计数脚本。
#
$counter_file = '/tmp/counter.txt';
clearstatcache();
ignore_user_abort(true); ## 阻止刷新中止文件操作并破坏文件。
如果 (file_exists($counter_file)) {
$fh = fopen($counter_file, 'r+');
当(1) {
如果 (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);
中断;
}
}
}
否则 {
$fh = fopen($counter_file, 'w+');
fwrite($fh, "1");
$buffer="1";
}
fclose($fh);
打印 "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 上表现相同。我还没有测试其他版本/操作系统组合。
请注意,你不能写入 HTTP 资源,例如进行 PUT 请求。
你会得到以下错误:'Failed to open stream: HTTP wrapper does not support writeable connections'
要执行 PUT,你只能填充 HTTP 上下文的 'content' 密钥,或者使用 Curl。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<?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>
"不要使用以下保留设备名称作为文件名称
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
下载:我需要一个函数来模拟 "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);
// 其余部分是标准的
?>
以 "r+" 模式打开一个文件,然后尝试在读取文件之前使用 ftruncate 设置文件指针位置,会导致文件数据丢失,就像您以 "w" 模式打开文件一样。
例如
$File = fopen($FilePath,"r+"); // 以读写模式打开文件
ftruncate($File, 0); // 设置指针位置(会擦除数据)
while(! feof($File)) { // 继续直到到达文件末尾
$Line = fgets($File); // 将文件中的行获取到字符串中
$Line = trim($Line); // 从字符串中修剪换行符
}
ftruncate($File,0); // (不会擦除数据)
fclose($File);
这里似乎没有说明,但请记住,当 $filename 包含空字节 (\0) 时,将会抛出 TypeError,消息如下;
TypeError: fopen() expects parameter 1 to be a valid path, string given in ...
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<?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>
在 Windows 上使用 ssl / https 时,我会遇到以下错误
"警告:fopen(https://example.com): 无法打开流:无效参数,位于 someSpecialFile.php 的第 4344534 行"
这是因为我没有启用 "php_openssl.dll" 扩展。
因此,如果您遇到同样的问题,请转到您的 php.ini 文件并启用它:)