先驱们钻研的干货


我在路上就在想,这么底层的一个特性,为什么 Python 没有为程序员处理掉呢?都用 Python 了,谁会为了那么大的便利牺牲一丁点性能呢?一回来我就做了测试。

这说明 Python 2 确实该换了!

再次更新:

找到原因之后我就觉得这问题在哪里见过,今天终于找出来了, python-cn 邮件列表里讨论过的。


真正的答案来啦~~我在 MSDN 里找得好苦哦 QAQ

Windows 怎么样了。经查源码(Python 2.7.6)Objects/fileobject.c:2837,Python 是使用 fopen/fread/fwrite 这系列函数来读写文件的。MSDN 说

When the "r+", "w+", or "a+" access type is specified, both reading and writing are allowed (the file is said to be open for "update"). However, when you switch from reading to writing, the input operation must encounter an EOF marker. If there is no EOF, you must use an intervening call to a file positioning function. The file positioning functions are fsetpos, fseek, and rewind. When you switch from writing to reading, you must use an intervening call to either fflush

所以会有 @沙渺 发现添加 flush()man 3 fopen

Reads and writes may be intermixed on read/write streams in any order. Note that ANSI C requires that a file positioning function intervene between output and input, unless an input operation encounters end-of-file. (If this condition is not met, then a read is allowed to return the result of writes other than the most recent.) Therefore it is good practice (and indeed sometimes necessary under Linux) to put an fseek(3) or fgetpos(3) operation between write and read operations on such a stream. This operation may be an apparent no-op (as in fseek(..., 0L, SEEK_CUR) called for its synchronizing side effect.

fflush()。)


请特别阅读 @依云 的答案。分析上游代码和文档,这才是真正坚不可摧的力量。

  • 看似跨平台但本质动作仍然平台有关的操作,真的千万少用。Python的跨平台特性,遇到了情况也惯坏人……
  • 在磁盘上跳来跳去真不安全,还是整批进整批出的把握。
  • 大量随机读写查询的需求,好好调动个高层的工具(数据库等)。

同仁们,误入歧途了啊!!!别在ASCII的文件上这么深入的追究编码问题啊!!!

我试了一下,果然爽翻:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
f = open(r"C:\Users\776\test.txt","w+") 
# 注:w+ truncates the file - 因此文件无论存在与否,结果一致
f.write('hello')
print(f.read())
f.close()
# 结果不贴了,同样不忍直视,传送门:http://paste.openstack.org/show/61806/

#!/usr/bin/env python
# -*- coding: utf-8 -*-
f = open(r"C:\Users\776\test.txt","w+") 
# 注:w+ truncates the file - 因此文件无论存在与否,结果一致
f.write('hello')
print(f.read())
f.close()
# 结果不贴了,同样不忍直视,传送门:http://paste.openstack.org/show/61806/

文件的指针位置。

open()以w+模式开启了一个读写模式的文件,由于是w,所以文件被废弃清空(truncate),此时的文件内容为[EOF],开启时的指针为0。此时如果做read(),则Python发现指针位置就是EOF,读取到空字符串。hello[EOF]

但看起来read()的时候,Python仍然去试图在磁盘的文件上,将指针从文件头向后跳5,再去读取到EOF为止。

[EOF]为止。所以最后得到了一些根本不期待的随机乱字符(这里根本不是编码问题造成的乱码!)。C:\Anaconda\神马的 :D)flush()方法,将已修改的文件内容可靠写盘:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
f = open(r"C:\Users\776\test.txt","w+") 
# 注:w+ truncates the file - 因此文件无论存在与否,结果一致
f.write('hello') # 此时指针=5,内存内容=`hello[EOF]`
f.flush() # 此时硬盘内容=`hello[EOF]`
print(f.read()) # 此时指针=5,正好在[EOF]上,正确输出''
f.seek(0) # 指针归0
print(f.read()) # 正确从头读出全部内容'hello'
f.close()

#!/usr/bin/env python
# -*- coding: utf-8 -*-
f = open(r"C:\Users\776\test.txt","w+") 
# 注:w+ truncates the file - 因此文件无论存在与否,结果一致
f.write('hello') # 此时指针=5,内存内容=`hello[EOF]`
f.flush() # 此时硬盘内容=`hello[EOF]`
print(f.read()) # 此时指针=5,正好在[EOF]上,正确输出''
f.seek(0) # 指针归0
print(f.read()) # 正确从头读出全部内容'hello'
f.close()

flush()应该是真正决定写盘之前才做的,而不是read()一次就flush()一次。read()似乎还是优先读取内存缓冲区更有道理。

但总之至少猜测到了一个原因,并一定程度解决问题,更好的方法有待补充吧?