我在附加模式下打开文件然后尝试寻找文件的开头时遇到了这种奇怪的行为。
代码应该是不言自明的:在第二次打开时,我希望能够在文件的开头写一个字符串然后让f.tell()
返回5(开头写的字节数)该文件)。
事实上,在Python 2.6.6和2.7.6中,最终的断言触发了,但令人惊讶的是,它在Python 3.3.2中起作用。
# Create new file, write after the 100th byte.
f = open("test", "w+b")
f.seek(100, 0)
f.write(b"end")
assert f.tell() == 100 + len("end")
f.close()
# Open file for writing without overwriting, attempt to write at start of file
# of the file, but doesn't.
f = open("test", "a+b")
f.seek(0, 0)
f.write(b"start")
assert f.tell() == len("start")
f.close()
所以我做了一个相同的C程序。它实际上表现得像Python 2.x版本:
#include <stdio.h>
int main() {
FILE *f = fopen("tt", "w+b");
fseek(f, 100, 0);
fwrite("x", 1, 1, f);
fclose(f);
f = fopen("tt", "a+b");
fseek(f, 0, 0);
fwrite("y", 1, 1, f);
printf("%ld\n", ftell(f));
fclose(f);
return 0;
}
这打印102
,我认为这是规范的(我也看过strace -eopen,close,lseek
输出,但不是更聪明的。)
所以我的问题是:我没有什么样的令人尴尬的基本知识?
为什么Python 3的行为有所不同?
顺便说一下,我在Linux上。
答案 0 :(得分:10)
行为是由您的操作系统强制执行的。
Python 2 open()
调用实际上是以C
代码的方式打开文件,稍微包装一下。两者都要求操作系统以给定模式打开文件。然后,您的操作系统明确限制您可以寻求的位置;您不是允许在打开文件之前寻求读取或覆盖那里的数据。
在Python 3中,I / O已经过大修,新的文件处理代码不直接将模式传递给操作系统,使您不受该限制的约束。您可以使用io.open()
function在Python 2中执行相同操作。
open()
function documentation当然会警告您在使用seek
时甚至无法使用a
的某些操作系统行为:
[...]
'a'
用于追加(在某些Unix系统上意味着所有写入都附加到文件的末尾而不管当前的搜索位置)。
在Linux上,open()
man page明确表达了这种行为:
O_APPEND
该文件以追加模式打开。在每个write(2)
之前, 文件偏移位于文件的末尾,就像使用lseek(2)
。
e.g。在尝试写入时,文件位置移动到文件的末尾;你只能被允许追加。
我希望找到一份以这种方式行事的操作系统列表;它看起来像Microsoft Windows做类似的事情。 .NET FileMode
enumeration documentation州:
Append
[...]尝试在文件结束之前寻找某个位置会引发IOException
异常,并且任何读取尝试都会失败并引发NotSupportedException
异常。
在任何情况下,如果您想要在打开文件时写入和读取文件(而不仅仅是追加),而不是截断文件,请使用'r+'
模式。这将打开文件进行读写。您可以通过搜索结束来追加,但与此同时,您可以自由地寻找文件中的任何一点。