python写文件打开后是乱码

windows7 +2.7.5
我的源代码:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
file = open(r"D:\Android\python\test.txt","w+")
file.write('hello')
file.read()

python test1.py后,用记事本打开文件显示乱码,请问这是怎么回事啊?

以下为截图

阅读 31.3k
5 个回答

更新:

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

经测试,Python 2.7.5 on Windows XP 重现此情况,Python 3.3.2 on Windows XP 没有重现。这说明 Python 2 确实该换了!

再次更新:

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


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

真正的原因不在于 Python 怎么样了,而在于 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 or to a file positioning function.

所以会有 @沙渺 发现添加 flush() 调用后正确的结果。

Linux 下没有重现。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.

所以 Windows 的这种行为是符合 ANSI C 标准的,但是 Linux 并不(总是)需要这样做。(并且,跨平台的方案是使用文件定位函数而不是 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/

我没有深入的究其原因,不过大概能猜到理由是什么:文件的指针位置

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

在写入hello之后,指针的位置是5,文件在内存中是hello[EOF]

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

也就是说,你实际上是跳过了该文件真正的EOF,为硬盘底层的数据做了一个dump,一直dump到了一个从前文件的[EOF]为止。所以最后得到了一些根本不期待的随机乱字符(这里根本不是编码问题造成的乱码!)。

(看起来似乎还暴露了一些以前文件的内容呢,C:\Anaconda\神马的 :D)

解决这个问题,你需要在读文件之前,用file对象的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()

这并不是完美的解决方法,因为我总觉得flush()应该是真正决定写盘之前才做的,而不是read()一次就flush()一次。read()似乎还是优先读取内存缓冲区更有道理。

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

Windows下的编码是个大坑,别用记事本了,换nodepad++吧。

乱码应该是报错信息,读文件之前先打开,最后还最好要 close

试试下面这个

#!/usr/bin/env python
# -*- coding: utf-8 -*-
file = open(r"D:\Android\python\test.txt","w+")
file.write('hello')

file = open("D:\Android\python\test.txt")
file.read()
新手上路,请多包涵

file文件的读写,一定要注意,在写完之后,必须要seek(0),把文件指针重新指向文件开头,然后再读,否则就会从缓冲区读取一大堆乱码——顺便,这是不是一个潜在的缓冲区溢出漏洞啊。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏