文件IO常用操作
column | column |
---|---|
open | 打开 |
read | 读取 |
write | 写入 |
close | 关闭 |
readline | 行读取 |
reaslines | 多行读取 |
seek | 文件指针操作 |
tell | 指针位置 |
打开操作
open(file,mode='r',buffering=-1,encoding=None,errors=None,neewline=None,closefd=True,opener = None)
打开一个文件,返回一个文件对象(流对象)和文件描述符。打开文件失败,则返回异常
基本使用:
创建一个文件test,然后打开它,用完关闭
f = open("test") #file对象
#windows<_io.TestIOWrapper name = 'test'mode='r' encoding='cp936'>
#linux <_io.TestIOWrapper name = 'test'mode='r' encoding='UTF-8'>
print(f.read()) #读取文件
f.close()#关闭文件
文件操作中,最常用的操作就是读和写
文件访问的模式有两种:文本模式和二进制模式
不同模式下,操作函数不尽相同,表现的结果也不一样
注: Windows中使用codepage代码页,可以认为每一个代码页就是一张编码表。cp936等同于GBK
open 的参数
file
打开或者要创建的文件夹名。如果不指定路径,默认是当前路径
mode 模式
描述字符 | 意义 |
---|---|
r | 缺省的,表示打开只读 文件不存在就抛FileNotFoundError异常 |
w | ①没有此文件,就创建新的文件 ②有此文件,先清空文件内容,再重头写 |
x | ①文件不存在,创建文件,并只写方式打开 ②文件存在,则抛FileExistsError异常 |
a | ①文件存在,写入打开,就追加 ②文件不存在,则创建后,只写打开,追加内容 |
b | 二进制模式 |
t | 缺省的,文本模式 |
+ | 读写打开一个文件 。给原来只读、只写方式打开提供缺失的读或者写 |
- r 是只读,wxa都是只写
- wxa模式都可以产生新文件夹
- w不管文件存在与否,都会生成全新内容的文件
- a 不管文件是否存在,都能在打开的文件尾部追加
- x 必须要求文件事先不存在,自己要造一个新文件
文件指针
文件指针:指向当前字节位置
mode = r 指针起始在0
mode = a 指针起始在EOF
tell() 显示指针当前位置
seek(offset [ , whence])
移动文件指针位置。offset 偏移多少字节,whence从哪里开始
文本模式下
whence 0 缺省值,表示从头开始,offset只能正整数
whence 1 表示从当前位置,offset只接受0
whence 2 表示从EOF开始,offset只接受0
#文本模式
f = open ('test4','r+')
f.tell() #开始
f.read()
f.tell() #EOF
f.seek(0) #起始
f.read()
f.seek(2,0)
f.read()
f,seek(2,0)
f.seek(2,1) # offset 必须为0
f.seek(2,2) #offset 必须为0
f.close()
#中文
f = open('test4','w+')
f.write('马哥教育')
f.tell()
f.close()
f = open('test4','r+')
f.read(2)
f.seek(1)
f.tell()
f.read()
f.seek(2) # f.seek(3)
f.close()
文本模式支持从头向后偏移的方式
whence为1 表示从当前位置开始偏移,但是只支持偏移0,相当于原地不动,所以就没什么用
whence为2表示从EOF开始,只支持偏移0,相当于移动文件指针到EOF
seek是按照字节偏移的
read在文本模式是按照字符读取的
二进制模式下
whence 0 缺省值 ,表示从头开始,offset只能读正整数
whence 1 表示从当前位置,offset可正可负
whence 2 表示从EOF开始,offset可正可负
#二进制模式
f = open ('test4','rb+')
f.tell() #起始
f.read()
f.tell() #EOF
f.write(b'abc')
f.seek(0) #起始
f.seek(2,1) # 从当前指针开始,向后2
f.read()
f.seek(-2,1) #从当前指针开始,向前2
f.seek(2,2) #从EOF开始,向后2
f.seek(0)
f.seek(-2,2) #从EOF开始,向前2
f.read()
f.seek(-20,2) #OSerror
f.close()
二进制模式支持任意起点的偏移,从头、从尾部、从中间位置开始
向后seek可以超界,但是向前seek的时候,不能超界,否则抛异常
seek之后,文件内容会立即写入磁盘
buffering 缓冲区
-1表示使用缺省大小的buffer。
如果是二进制模式,使用io.DEFAULT_BUFFER_SIZE值,默认是4096或者8192.
如果是文本模式,如果是终端设备,是行缓存方式,如果不是,则使用二进制模式的策略
- 0,只在二进制模式使用,表示关buffer
- 1,只在文本模式使用,表示使用行缓冲。意思就是见到换行符就flush
- 大于1 , 用于指定buffer的大小
缓冲区是一个内存空间,一般来说是一个FIFO队列,到缓冲区满了或者达到阈值,数据才会flush到磁盘
flush() 将缓冲区数据写入磁盘
close()关闭之前会调用flush()
io.DEFAULT_BUFFER_SIZE缺省缓冲区取大小、字节
#二进制模式
import io
#缺省缓冲区大小
f = open ('test4','w+b')
print(io.DEFAULT_BUFFER_SIZE) #python 为8192
f.write('www.magedu.com'.encode())
f.flush()
f.close()
# 缓冲区大小为0
f = open('test4', 'w+b', 0)
f.write(b'a')
# cat test4
f.write(b'a')
# cat test4
f.close()
# 缓冲区大小为1
f = open('test4', 'w+b', 1)
f.write(b'a')
# cat test4
f.write(b'a' * 200)
# cat test4
f.write(b'a' * 8000)
# cat test4
f.close()
# 设置缓冲区大小
f = open('test4', 'W+b', 4)
f.write(b'mag')
# cat test4
f.write(b'edu')
# cat test4
f.close()
二进制模式:
- -1 是缺省值,使用缺省缓冲区大小
- 0关闭缓冲区,不需要内存的buffer,可以看作是一个FIFO的文件
- 1不支持,直接使用缺省缓冲区大小
- 大于1 ,使用指定缓冲区大小
文本模式
# buffering=0, 行吗?
# buffering=1,使用行缓冲
f = open('test4', 'w+', 1)
f.write('mag') # cat test4
f.write('magedu' * 4) # cat test4
f.write('\n') # cat test4
f.write('Hello\nPython') # cat test4
f.close()
f = open('test4', 'w+', 1)
f.write('a' * 3000)
f.write('a' * 3000)
# cat test4
f.write('a' * 3000)
# cat test4
f.close()
# buffering>1,使用指定大小的缓冲区
f = open('test4', 'w+', 15)
f.write('mag') # cat test4
f.write('edu')
f.write('Hello\n')
f.write('\nPython')
f.write('a' * (io.DEFAULT_BUFFER_SIZE - 20)) # 设置为大于1没什么用
f.write('\nwww.magedu.com/python')
f.close()
buffeing | 说明 |
---|---|
buffering = 1 | t和b 都是io.DEFAULT_BUFFER_SIZE |
buffering = 0 | b 关闭缓冲区 t 不支持 |
buffering = 1 | t 行缓冲 遇到换行符才flush |
buffering > 1 | b模式表示缓冲大小,缓冲区的值可以超过io.DEFAULT_BUFFER_SIZE,直到设定的值超出后才把缓冲区flush t 模式,是io.DEFAULT_BUFFER_SIZE字节,flush完后把当前字符串也写入磁盘 |
一般来说,只需要记得:
- 文本模式,一般都用默认缓冲区大小
- 二进制模式,是一个个字节的操作 ,可以指定buffer大小
- 一般来说,默认缓冲区大小是个比较好的选择,除非明确知道,否则不调整它
- 一般编程中,明确知道需要写磁盘了,都会手动调试一次flush,而不是等到自动flush或者close
encoding:编码,仅文本模式使用
None表示使用缺省编码,以来操作系统。
Windows、Linux下测试如下代码
f = open('test1','w')
f.write('啊')
f.close()
Windows下缺省GBK(oxB0A1),Linux下缺省UTF-8(oxE5 95 8A)
其他参数
-
errors
None和strick 表示有错误编码将抛出ValueError异常;ignore表示忽略
-
newline
文本模式,换行的转换。可以用None、空字符串、’\r’、’\n’、’\r\n’
读时
- None 表示’\r’、’\n’、’\r\n’都被转换成’\n’
- ''表示不会自动转换通用换行符,常用换行符都可以是换行
- 其他合法换行字符表示按照字符分行
写时
- None表示’\n’都会被替换为系统缺省行分隔符os.linesep
- ‘\n’或’’ 表示 ‘\n’ 不替换
- 其他合法换行字符表示文本中的’\n’ 换行符会被替换为指定的换行字符
f = open('o:/test','w')
#newline 缺省为 None ,Windows下回吧\n替换为\r\n
f.write('python\rwww.python.org\nwww.magedu.com\r\npython3')
#真正写入的是
#'python\rwww.python.org\r\nwww.magedu.com\r\r\npython3'
f.close()
newlines = ['',None,'\n','\r\n']
for nl in newline:
f = open('o:/test',newline = nl)# 缺省替换所有换行符
print(f.readlines())
f.close()
#运行结果如下
['python\r', 'www.python.org\r\n', 'www.magedu.com\r', '\r\n', 'python3']
['python\n', 'www.python.org\n', '\nwww.magedu.com\n', '\n', 'python3']
['python\rwww.python.org\r\n', 'www.magedu.com\r\r\n', 'python3']
['python\rwww.python.org\r\n', 'www.magedu.com\r\r\n', 'python3']
closed
关闭文件描述,True表示关闭它。False会在文件关闭后保持这个描述符。 fileobj.fileno()查看
read
read(size = -1)
size 表示读取的多少个字符或字节;负数或None表示读取到EOF
f = open('o:/test4','r+')
f.write('magedu')
f.write('\n')
f.write('马哥教育')
f.seek(0)
f.read(7)
f.close()
# 二进制
f = open('test4','rb+')
f.read(7)
f.read(1)
f.close()
行读取
readline(size=-1)
一行行读取文件内容。size设置一次能读取行内几个字符或字节
readlines()
读取所有行的列表
#按行迭代
f = open('test') # 返回可迭代对象
for line in f:
print(line.encode()) #带换行符,有必要请strip
f.close()
write
write(s) , 把字符串s写入到文件中并返回字符的个数
writelines(lines),将字符串列表写入文件
f = open('test','w+')
lines = ['abc','123\n','magedu'] #需提供换行符
f.writeline(lines)
f.seek(0)
print(f.read())
f.close()
close
flush并关闭文件对象
文件已将关闭,再次关闭没有任何效果
其他
名称 | 说明 |
---|---|
seekable() | 是否可以seek |
readable() | 是否可读 |
writable() | 是否可写 |
closed | 是否已经关闭 |
上下文管理
问题的引出
在Linux中,执行
#下面必须这么写
lst = []
for _ in range(2000):
lst.append(open('test'))
#OSError :[Errno 24]Too many open files :'test'
print(len(lst))
lsof 列表打开的文件。没有该命令就安装 #yum install lsof
$ ps aux | grep python
$ lsof -p 9255 | grep test | wc -1
$ ulimit -a
ps命令返回进程,grep出python进程ID
lsof -p 进程号,列出该进程的所有文件描述符,grep出test文件文件描述符,wc统计
nlimit -a 查看所有限制。其中open files 就是打开文件数的限制,默认1024
for x in lst:
x.close()
将文件一次性关闭,然后就可以继续打开了。再看一次lsof
如何解决?
1、异常处理
当出现异常的时候,拦截异常。但是,因为很多代码都可能出现OSError异常,还不好判断异常就是应为资源限制
产生的
f = open ('test')
print(f.closed)
try:
f,write("abc") #文件只读,写入失败
finally:
f.close() #这样也行
使用finally可以保证打开的文件可以被关闭
2、上下文管理
一种特殊的语法,交给解释器去释放文件对象
上下文管理
def f #删除前面定义的f,如果没有则不用删除
with open('test') as f:
print(f,f.closed)
f.write("abc") # 文件只读,写入失败
#测试f是否关闭
f.closed #f的作用域
上下文管理
- 使用with…as 关键字
- 上下文管理的语句块并不会开启新的作用域
- with语句块执行完的时候,会自动关闭文件对象
另一种写法
f1 = open('test')
with f1:
f1.write('abc') # 文件只读,写入失败
#测试f是否关闭
f1.closed # f1的作用域
- 对于类似文件对象的IO对象,一般来说都需要在不使用的时候关闭、注销,以释放资源
- IO被打开的时候,会获得一个文件描述符。计算机资源是有限的,所以操作系统都会被限制。就是为了保护计算机的资源不要被完全耗尽,计算资源是很多程序共享的、不是独占的
- 一般情况下,除非特别明确的知道当前资源情况,否则不要盲目提高资源的限制值来解决问题
StringIO和BytesIO
StringIO
-
IO模块中的类
- from io import StringIO
-
内存中,开辟的一个文本模式的buffer,可以像文件对象一样操作它
-
当close方法被调用的时候,这个buffer会被释放
getvalue() 获取全部内容。跟文件指针没有关系
from io import StringIO
# 内存中构建
sio = StringIO() # 像文件对象一样操作
print(sio.readable(),sio.writable(),sio.seekable())
sio.write("magedu\npython")
sio.seek(0)
print(sio.readline())
print(sio.getvalue()) # 无视指针,输出全部内容
好处:
一般来说,磁盘的操作比内存的操作要慢得多,内存足够的情况下,一般的优化思路是少落地,减少磁盘IO的过程,可以大大提高程序的效率
BytesIO
-
IO模块中的类
- from io import Bytes
-
内存中,开辟的一个二进制模式的buffer,可以像文件对象一样操作它
-
当close方法被调用时,这个buffer会被释放
from io import BytesIO #内存中构建
bio = BytesIO()
print(bio.readable,bio.writable,bio.seekable())
bio.write(b'magedu\npython')
bio.seek(0)
print(bio.readline())
print(bio.getvalue()) #无视指针,输出全部内容
bio.close()