注意 - 在 'w' 模式下使用 fopen 不會像您預期的那樣更新檔案的修改時間 (filemtime)。您可能需要在寫入並關閉檔案後發出 touch(),以更新其修改時間。如果您打算保住頭髮,這在快取情況下可能變得至關重要。
(PHP 4、PHP 5、PHP 7、PHP 8)
fopen — 開啟檔案或 URL
fopen() 將 filename
指定的具名資源繫結到一個串流。
filename
如果 filename
的格式為「scheme://...」,則會假定其為 URL,而 PHP 將會為該 scheme 搜尋通訊協定處理常式(也稱為封裝器)。如果沒有註冊該通訊協定的封裝器,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' |
在開啟的檔案描述子設定 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:password@example.com/somefile.txt", "w");
?>
當使用 SSL 時,Microsoft IIS 會違反協定,在未傳送 close_notify
指示符的情況下關閉連線。當您到達資料末尾時,PHP 會將此回報為「SSL: Fatal Protocol Error」。為了避免此問題,應將 error_reporting 的值降低到不包含警告的層級。當您使用 https://
封裝器開啟串流時,PHP 可以偵測到有錯誤的 IIS 伺服器軟體,並會抑制警告。當使用 fsockopen() 建立 ssl://
socket 時,開發人員有責任偵測並抑制此警告。
注意:
如果您在使用 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:buttercup@example.com//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 上,您可以順利讀取原生檔案系統的目錄格式。
在開啟包含多位元組資料 (例如: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 支援對 fifo 進行非阻擋的 `fopen()`,但 PHP 不支援)。
其結果是,你無法得知最初的 fifo 讀/寫是否會被阻擋,因為要做到這點你需要 `stream_select()`,而 `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 socket
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);