PHP 日本大会 2024

fopen

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

fopen打开文件或 URL

描述

fopen(
    字符串 $filename,
    字符串 $mode,
    布尔值 $use_include_path = false,
    ?资源 $context = null
): 资源|false

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.iniuser_agenthttp 包装器使用)。

在 Windows 平台上,请注意转义路径中使用的任何反斜杠,或使用正斜杠。

<?php
$handle
= fopen("c:\\folder\\resource.txt", "r");
?>

mode

mode 参数指定您需要对流的访问类型。它可以是以下任何一个

使用 modefopen() 可能的模式列表
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://outputphp://inputphp://stdinphp://stdoutphp://stderrphp://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()函数。

参见

添加注释

用户贡献的注释 21条注释

chapman at worldtakeoverindustries dot com
12年前
注意 - 使用'w'模式下的fopen不会像您预期的那样更新文件的修改时间(filemtime)。您可能希望在写入和关闭文件后发出touch(),这将更新其修改时间。如果您想保持您的头发,这在缓存情况下可能变得至关重要。
匿名
4年前
/***** 友好提醒 *****/
非常重要。除非您想删除文件中的所有内容,否则请不要使用“w”标志。
php-manual at merlindynamics dot com
4年前
有一种未公开的模式可以使fopen非阻塞(在Windows上不起作用)。通过在模式参数中添加'n',fopen将不会阻塞,但是如果管道不存在,则会引发错误。

$fp = fopen("/tmp/debug", "a"); //如果管道不存在则阻塞

$fp = fopen("/tmp/debug", "an"); //管道不存在则引发错误
php at delhelsa dot com
16年前
在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的主目录的相对路径。
petepostma-deletethis at gmail dot com
7年前
阅读文字描述以了解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,开头,,,"打开/创建文件,先读取,然后写入"
ideacode
19年前
请注意,您是否可以打开目录取决于操作系统。以下几行

<?php
// Windows ($fh === false)
$fh = fopen('c:\\Temp', 'r');

// UNIX (is_resource($fh) === true)
$fh = fopen('/tmp', 'r');
?>

表明在Windows(2000,可能是XP)上,您可能无法打开目录(错误是“权限被拒绝”),无论该目录的安全权限如何。

在UNIX上,您可以愉快地读取原生文件系统的目录格式。
splogamurugan at gmail dot com
13年前
打开包含多字节数据的文件(例如: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";
}
}
?>

希望对您有所帮助。
durwood at speakeasy dot NOSPAM dot net
19年前
当我将服务器迁移到新的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

希望这对其他人有所帮助——我花了很长时间才找到这个问题。
php at richardneill dot org
13年前
如果要打开的文件是命名管道 (FIFO),则 fopen() 将阻塞。无论以 "r" 还是 "w" 模式打开都是如此。(参见 man 7 fifo:这是正确、默认的行为;尽管 Linux 支持对命名管道的非阻塞 fopen(),但 PHP 不支持)。
这样做的结果是,你无法发现初始的命名管道读/写是否会阻塞,因为要做到这一点,你需要 stream_select(),而这反过来又需要 fopen() 已经执行!
etters dot ayoub at gmail dot com
6年前
此函数在创建文件夹之前检查递归权限和父文件夹的递归存在性。以避免生成错误/警告。

/**
* 此函数检查递归权限和父文件夹的递归存在性,
* 在创建文件夹之前。以避免生成错误/警告。
*
* @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;
}
dan at cleandns dot com
21年前
<?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";

?>
info at b1g dot de
19年前
一个简单的类,用于获取 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);
}
}
}
?>
[email protected]
12年前
使用fopen()时,可以使用变量$http_response_header,它包含一个响应头的数组。
[email protected]
21年前
如果提供没有文件名的路径,PHP 将打开一个目录。我刚刚被这个问题困扰了。我没有检查连接字符串的文件名部分。

例如

<?php
$fd
= fopen('/home/mydir/' . $somefile, 'r');
?>

如果$somefile = '',则将打开目录。

如果尝试使用文件句柄读取,您将获得二进制目录内容。我尝试了追加模式,它会出错,所以似乎并不危险。

这是在 FreeBSD 4.5 和 PHP 4.3.1 上的测试结果。在 4.1.1 和 PHP 4.1.2 上的行为相同。我没有测试其他版本/操作系统组合。
[email protected]
12年前
"不要使用以下保留的设备名称作为文件名
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
[email protected]
2 年前
<!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>
flobee
18年前
下载:我需要一个函数来模拟“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;
}
?>
keithm at aoeex dot NOSPAM dot com
23年前
我正在为 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);
?>
ceo at l-i-e dot com
18年前
如果您需要对 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);
// 其余部分是标准的
?>
bohwaz
10个月前
请注意,您无法写入 HTTP 资源,例如执行 PUT 请求。

您将收到此错误:'Failed to open stream: HTTP wrapper does not support writeable connections'

要执行 PUT 操作,您只能填充 HTTP 上下文的“content”键,或者使用 Curl。
Derrick
1年前
以“r+”模式打开文件,然后尝试在读取文件之前使用 ftruncate 设置文件指针位置将导致文件数据丢失,就像您以“w”模式打开文件一样。

例如

$File = fopen($FilePath,"r+"); // 以读写模式打开文件

ftruncate($File, 0); // 设置指针位置(将擦除数据)

while(! feof($File)) { // 继续直到到达文件结尾

$Line = fgets($File); // 将文件的一行读取到字符串中
$Line = trim($Line); // 修剪字符串的换行符
}

ftruncate($File,0); // (将不会擦除数据)

fclose($File);
To Top