在PHP中,fopen模式“r+”和“rw+”有什么区别?

61
在Stack Overflow上,许多答案使用fopen($file, "rw+"),但是手册中没有列出"rw+"模式,只有"r+"模式(或"w+"模式)。
因此我想知道:"rw+"模式是什么?fopen($file,"rw+""r+"之间的区别是什么?因为没有关于"rw+"模式的文档。 一种方法是考虑这些模式是“可加的”,但是我在fopen手册页面中找不到任何结合模式的提及(此外,如果"w+"已经使其可读,那么将"r""w+"相结合的意义是什么?)。但最重要的是,w+模式截断文件,而rw+不截断文件(因此,它们不能是可加的)。可能并没有rw+模式,尽管Stack Overflow用户使用了它。也许它可以工作是因为解析器忽略了"w"字母,因为rw+模式似乎等同于r+
为了澄清我的问题:"rw+"模式是什么,也就是说它与其他模式有何区别?在评论中,我只收到两个回答:要么是告诉我检查文档(我已经检查过了),要么是错误的答案,称其等同于"w+"(它并不是)。"w+"截断文件,而"rw+"则不会。
以下是用于测试的脚本(它证明了w+截取了文件,但rw+没有):
<?php 
$file = "somefile"; 
$fileOpened = fopen($file, "w"); 
fwrite($fileOpened, "0000000000000000000"); 
fclose($fileOpened);  

$fileOpened = fopen($file, "rw+"); 
fwrite($fileOpened, "data"); 
fclose($fileOpened); 
$fileOpened = fopen($file, "r"); 
$output = fgets($fileOpened); 
echo "First, with 'rw+' mode:<br>".$output; 

$fileOpened = fopen($file, "w+"); 
fwrite($fileOpened, "data"); 
fclose($fileOpened); 
$fileOpened = fopen($file, "r"); 
$output = fgets($fileOpened); 
echo "<br><br><br>Now with only 'w+' mode:<br>".$output; 
?>

2
手册确实列出了这些。它们是rw+。这只是一个组合。 - M. Eriksson
1
我已经投了一张重新开启的票,因为我同意重新开启的请求(也许是有点微妙,但我认为它有一个具体的答案)。但请不要用帖子来抱怨投票决定,特别是如果你的语气很生气或讽刺 - 那不属于这里。如果您希望申诉投票决定,我们鼓励在 Meta 网站上进行(当发帖人对关闭原因不满意时)。 - halfer
1
感谢您的回复@halfer!不幸的是,“rw+”并不等同于“w+”加上“r”模式。从我的测试来看,它似乎与“r+”模式相同,并且与“w”模式无关。如果我的问题仍然不清楚,我很抱歉,您能告诉我什么使它成为一个边缘案例吗?对我来说很清楚,因为我想知道未记录的“rw+”模式是做什么的。我需要在本周末完成一项工作,但我很快就会编辑我的问题并发布到Meta网站上。 - flen
2
感谢@halfer的编辑!我还在忙于交付尽快需要完成的任务,但我会在本周进行编辑。主要测试非常简单:如果我将rw+替换为w+,我的代码就会崩溃,但是如果我将其替换为r+,它就会像预期的那样正常工作。原因很简单:w+截断文件,但rw+不会,它的行为与r+完全相同(不截断文件)。我对此进行了一些其他测试,但都是基本的东西,总是得到rw+ === r+ - flen
5
这个问题正在这个元问题中讨论。 - halfer
显示剩余6条评论
1个回答

74
我想你已经基本明白了。虽然我猜,你想要权威的答案。
文档确实没有提到 "rw+"。但是,有更好的东西:PHP源代码

兔子洞

在我找到有关 PHP 开发人员的 PHP 源代码系列文章(第 1 部分, 第 2 部分, 第 3 部分, 第 4 部分)之前,我很难尝试导航源代码。 我知道这些链接跳转到两个网站之间,该系列文章就是如此时髦。顺便说一句,我没有找到宣布的第 5 部分。

注意:这些文章有点旧了,它们讨论的是 PHP 5.4。

让我们看看 fopen 实际上是做什么的... 让我们进入兔子洞...

首先我们看一下函数定义(我是按照上面链接的第二部分的建议找到它的)。我注意到它使用了php_stream_open_wrapper_ex,而我在其他地方发现它只使用了_php_stream_open_wrapper_ex,我们可以在stream.c中找到它。 _php_stream_open_wrapper_ex对于mode做了什么?它将其传递给stream_opener
寻找 stream_opener 的定义,将我引向了 php_streams.h 中的 php_stream_wrapper_ops 类型搜索使用 php_stream_wrapper_ops 类型的地方,带我来到了 plain_wrapper.c 文件。
实际上,有很多初始化 php_stream_wrapper_ops 的方法可以打开不同的东西。我们正在查看 php_fopen_wrapper.c,因为它有一个 php_stream_wrapper_ops 的初始化,其中 stream_openerphp_plain_files_stream_opener我们正在接近目标...

php_plain_files_stream_opener同一文件中进一步的说明它委托给php_stream_fopen_rel

php_streams.h定义了php_stream_fopen_rel,使用_php_stream_fopen。这个函数在plain_wrapper.c中

最后,_php_stream_fopen调用php_stream_parse_fopen_modes。将字符串解析为一些标志,太好了!


仙境

让我们来看看php_stream_parse_fopen_modes函数:

PHPAPI int php_stream_parse_fopen_modes(const char *mode, int *open_flags)
{
    int flags;

    switch (mode[0]) {
        case 'r':
            flags = 0;
            break;
        case 'w':
            flags = O_TRUNC|O_CREAT;
            break;
        case 'a':
            flags = O_CREAT|O_APPEND;
            break;
        case 'x':
            flags = O_CREAT|O_EXCL;
            break;
        case 'c':
            flags = O_CREAT;
            break;
        default:
            /* unknown mode */
            return FAILURE;
    }

    if (strchr(mode, '+')) {
        flags |= O_RDWR;
    } else if (flags) {
        flags |= O_WRONLY;
    } else {
        flags |= O_RDONLY;
    }

#if defined(O_CLOEXEC)
    if (strchr(mode, 'e')) {
        flags |= O_CLOEXEC;
    }
#endif

#if defined(O_NONBLOCK)
    if (strchr(mode, 'n')) {
        flags |= O_NONBLOCK;
    }
#endif

#if defined(_O_TEXT) && defined(O_BINARY)
    if (strchr(mode, 't')) {
        flags |= _O_TEXT;
    } else {
        flags |= O_BINARY;
    }
#endif

    *open_flags = flags;
    return SUCCESS;
}

对于抽象概念,它的功能如下(忽略细节):

  1. 它获取mode的第一个字符并检查它是否为rwaxc中的任何一个,如果识别到任何一个,则设置相应的标志。否则,我们有一个FAILURE

  2. 它在字符串中寻找+并设置相应的标志。

  3. 它在字符串中寻找ent(取决于预处理指令)并设置相应的标志。

  4. 返回SUCCESS


回到现实世界

您问:

PHP中fopen模式“r+”和“rw+”之间有什么区别?

没有区别。PHP只关心字符串以"r"开头并带有"+""w"被忽略。

最后注意:虽然尝试使用"read+"等写法很诱人,但要小心,因为这些字母可能在某一天具有某种含义。 它将不具有向前兼容性。 实际上,在某些情况下,"e"已经具有意义。 相反,建议遵循文档。


感谢您提供的机会让我查看PHP源代码。

11
技术上讲,你可以使用 "read+" 或 "rrrrrrrrrrrrrrrrrrrrrrrrrr+" 这样的字符串。 - Tas
5
只有不包含字母 ent 的单词才等同于简单的字母 r。所以 "rrrrrrrrrrrrr" 是符合要求的,但是 "read" 或者 @Theraot 或 @Nisse 建议中的任何单词都不符合要求,除非你确实需要额外的标志。 - alexis
6
我知道我应该避免留下“感谢”评论,但是这个答案太棒了,非常感谢您!它还揭示了其他语言(例如C语言,在那里我认为解析器也忽略了“w”https://adelekeadelaja.blogspot.com.br/2015/06/c-programming-file-rw-mode-not-working.html),我认为人们自然而然地选择“rw”,因为它更符合直觉,容易想到“读写”,但是他们忘记了加号(我认为普遍来说,“w+”意味着“读取”)。但我偏离了主题,再次感谢你! - flen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接