以附加模式打开文件并寻求开始

Opening file in append mode and seeking to start

我遇到过这种关于以附加模式打开文件然后尝试查找文件开头的奇怪行为。

代码应该是不言自明的:在第二次打开时,我希望能够将一个字符串写入文件的开头然后有 f.tell() return 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 输出,但 none 更聪明)。

所以我的问题是:我没有什么令人尴尬的基础知识?

为什么 Python 3 的行为不同?

顺便说一句,我在Linux。

该行为是您的 OS.

强制执行的行为

Python 2 open() 调用实际上打开文件的方式与您的 C 代码相同,只是多了一点包装。两者都要求 OS 以给定模式打开文件。您的 OS 然后 明确限制了 您可以搜索的位置; 不允许尝试读取或覆盖打开文件之前存在的数据。

在 Python 3 I/O 中进行了大修,新的文件处理代码不会直接将模式传递给 OS,让您不受该限制的约束.您可以在 Python 2 中使用 io.open() function.

执行相同的操作

open() function documentation 肯定会警告您某些 OS 行为,您在使用 a 时甚至不能使用 seek:

[...] 'a' for appending (which on some Unix systems means that all writes append to the end of the file regardless of the current seek position).

在 Linux 上,open() man page 明确说明了这种行为:

O_APPEND
The file is opened in append mode. Before each write(2), the file offset is positioned at the end of the file, as if with lseek(2).

例如尝试写入时,文件位置移动到文件末尾;你只能追加。

我想为您找到一个 OS 具有这种行为方式的列表;看起来 Microsoft Windows 做了类似的事情。 .NET FileMode enumeration documentation 状态:

Append
[...] Trying to seek to a position before the end of the file throws an IOException exception, and any attempt to read fails and throws a NotSupportedException exception.

在任何情况下,如果您希望同时写入和读取文件(不仅仅是追加),而不是 truncate 打开文件,请使用 'r+' 模式。这将打开文件进行读写。您可以通过定位追加到末尾,但同时您可以自由定位到文件中的任何位置。