open
Open Mode
Python中打开文件可以通过open
内置函数打开,但是需要注意的是open
函数用很多种模式,不同的模式使用起来会有点不同,官方的描述
Character | Meaning |
---|---|
r | open for reading (default) |
w | open for writing, truncating the file first |
x | create a new file and open it for writing |
a | open for writing, appending to the end of the file if it exists |
b | binary mode |
t | text mode (default) |
+ | open a disk file for updating (reading and writing) |
U | universal newline mode (deprecated) |
下面是自己的总结
模式 r
- 只读
- 文件指针 0
- 不会创建文件
模式 w
- 只写
- 文件指针 0
- 清空文件
- 创建文件
模式 a
- 追加
- 文件指针 end
- 不清空文件
- 创建文件
模式 x
- 创建文件
- 写入
- 文件指针 0
- 文件存在返回异常
变化 +
- 可读写
变化 b
- 二进制模式打开
- 按字节操作
变化 t
- 文本模式打开
- 按字符操作
Open buffering
官方的说明
buffering is an optional integer used to set the buffering policy.
Pass 0
to switch buffering off
(only allowed in binary mode), 1
to select line buffering
(only usable in text mode), and an integer > 1 to indicate the size of a fixed-size chunk buffer.
When no buffering argument is given, the default buffering policy works as follows:
Binary files are buffered in fixed-size chunks; the size of the buffer is chosen using a heuristic trying to determine the underlying device's "block size" and falling back on io.DEFAULT_BUFFER_SIZE
. On many systems, the buffer will typically be 4096
or 8192
bytes long.
"Interactive" text files (files for which isatty() returns True) use line buffering. Other text files use the policy described above for binary files.
简单来说就是
0
是关闭buffering
功能, 只能用于b
模式1
是开启line buffering
,只能用于t
模式>1
是表示使用固定大小的chunk buffer
当没指定buffering
参数时,默认将会使用下面两种策略
- 二进制文件 使用固定大小的
chunk buffer
,在系统中一般为4096或者8192,具体指是多少,可以通过io.DEFAULT_BUFFER_SIZE
查看 - 文本文件 通过isatty() 方法检测文件是否连接到一个终端设备,如果为
True的
话,则使用line buffering
,False
的话则采用上面的策略
buffering(binary) Practice
先看下不开启buffering
的效果
In [16]: f = open('utf8.txt', 'ab', buffering=0)
In [17]: f.write(b'A')
Out[17]: 1
In [18]: cat utf8.txt
敛青
A
In [19]: f.close()
可以看到在我write
之后里面就可看到数据被写入到文件里了
接下来再看下使用默认策略,buffering
的效果
In [20]: f = open('utf8.txt', 'ab')
In [21]: f.write(b'B')
Out[21]: 1
In [22]: cat utf8.txt
敛青
A
In [23]: f.close()
In [24]: cat utf8.txt
敛青
AB
可以看到,在我write
之后并没有看到新写入的B
,而是在我close
之后再能看到,说明在我write
的时候,并没有写入到磁盘,而是写入到了buffer
中
接下来看下系统默认的buffer size
In [25]: import io
In [26]: io.DEFAULT_BUFFER_SIZE
Out[26]: 8192
可以看到是8192
字节,我们在尝试下输入8192
个字符看下,是什么情况
In [28]: f = open('utf8.txt', 'ab')
In [29]: f.write(b'C' * 8192)
Out[29]: 8192
In [30]: cat utf8.txt
敛青
ABCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC....太长就不打印了
In [32]: f.close()
可以看到当我们写入的数据大于等于8192
字节时,缓冲区满了后,操作系统会把缓冲区数据flush到硬盘
通过上面的小实验可以看到,在开启buffering
时,当我们写数据时,默认情况下,首先会写到系统的buffer
中,此时有两种情况
- 未写满缓存区:未写满缓冲区时,只有在
f.close()
后数据才会flush
到磁盘,所以此时我们是无法读取到新写入的内容 - 写满缓冲区:当缓冲区写满后,缓存区的数据会立即
flush
到磁盘上
buffering(text) 小实验
以text模式打开的时候是line buffering
,简单来说就是遇到换行符就会flush
,
In [57]: f = open('utf8.txt', 'wt', buffering=1)
In [58]: cat utf8.txt
In [59]: f.write('123')
Out[59]: 3
In [60]: cat utf8.txt
In [61]: f.write('abc\n')
Out[61]: 4
In [62]: cat utf8.txt
123abc
虽然说只有碰到换行符才flush
,不过我这里发现,如果超过buffer size
,也会触发flush
In [63]: f.write('abc' * 10000)
Out[63]: 30000
In [64]: cat utf8.txt
123abc
abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabca...
In [67]: f.close()
Buffesr Size 调整
调整Buffer Size:
open('utf8.txt', 'ab', buffering=10000)
当然我们可以手动调整buffering size
,这里的调整只是针对此次IO,不会影响到系统,同时要想明白buffer size
大小所带来的影响
增大Buffer Size
- 写入性能得到提高
- 系统掉电或者进程crash,缓冲去数据丢失
调小(禁用)Buffer Size
- 写入性能会有损耗
- 丢失数据的可能几率会降低很多
一般来说为了性能考虑都是为开启buffering
的,但是有一个场景需要注意一下,假如我们进程之间如果通过文件来进行数据交互的话,此时我们应该把buffering
设置为0
,不然很有可能出现进程间无法及时获取到数据的情况
有关flush的疑惑
按照道理来说,flush
操作应该会把缓冲区里的所有数据都刷到磁盘,但是从现象上来看,好像只是把第一次的写入闪盘
In [1]: f=open('utf8.txt', 'wb', buffering=10)
In [2]: cat utf8.txt
In [3]: f.write(b'A'*8)
Out[3]: 8
In [4]: cat utf8.txt
In [5]: f.write(b'B'*8)
Out[5]: 8
In [6]: cat utf8.txt
AAAAAAAA
open encoding
encoding
指定文件读取写入的字符集(text mode)
创建文件并使用gbk
字符集
In [4]: f=open('gbk.txt', 'w', encoding='gbk')
In [5]: f.write('敛青')
Out[5]: 2
In [6]: f.close()
这里,我们尝试不指定encoding
,而是使用默认的看下是否能够正常读取
In [12]: f=open('gbk.txt', 'rt')
In [13]: f.read()
---------------------------------------------------------------------------
UnicodeDecodeError Traceback (most recent call last)
<ipython-input-13-bacd0e0f09a3> in <module>()
----> 1 f.read()
/usr/local/lib/python3.4/codecs.py in decode(self, input, final)
317 # decode input (taking the buffer into account)
318 data = self.buffer + input
--> 319 (result, consumed) = self._buffer_decode(data, self.errors, final)
320 # keep undecoded input until the next call
321 self.buffer = data[consumed:]
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc1 in position 0: invalid start byte
In [14]: f
Out[14]: <_io.TextIOWrapper name='gbk.txt' mode='rt' encoding='UTF-8'>
可以看到默认应该使用utf-8
来decode
的
现在我们手动指定下encodnig
再读取试试看
In [16]: f=open('gbk.txt', 'rt', encoding='gbk')
In [17]: f.read()
Out[17]: '敛青'
open errors
errors有两种模式,strict
模式和ignore
模式,默认是strict
模式(text mode)
strict
模式:抛出异常ignore
模式:忽略异常
手动设置一下ignore
模式看下
In [21]: f=open('gbk.txt', 'rt', errors='ignore')
In [22]: f.read()
Out[22]: ''
In [23]: f
Out[23]: <_io.TextIOWrapper name='gbk.txt' mode='rt' encoding='UTF-8'>
In [24]: f.close()
可以看到设置为ignore
模式后,之前解码时抛出的UnicodeDecodeError
异常就被忽略掉了看不见了。
open newline
主要是用来指定换行符,newline
可以接受'\n'
'\r'
and '\r\n'
作为值
open closefd
在Python里,每次open
一个文件时,都会打开一个文件描述符(fd
),在老版本的时候,文件描述符不会自动关闭,所以可以经常看到这样的写法
with open('filename') as f:
do something.
但是在现在的版本默认就支持并开启了自动关闭文件描述符的功能,所以可以偷个懒不用写f.close()
了