文件IO常见操作,open()函数,open函数各个参数(mode、buffering、encoding、errors、newline、closefd)、文件指针、缓冲区、上下文管理一一介绍并举例。
文件IO常见操作:
Open()函数:
open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
打开一个文件,返回一个文件对象(流对象)和文件描述符。打开文件失败,则返回异常基本使用:
创建一个文件test,然后打开它,用完关闭
参数:file
open 的file 参数:
打开或者要创建的文件名。如果不指定路径,默认是当前路径
In [1]: f = open('c:\\test.txt') #file对象,文件不存在返回异常 FileNotFoundError: [Errno 2] No such file or directory: 'c:\\test.txt'In [2]: f = open('c:\\test') #打开文件In [3]: fOut[3]: <_io.TextIOWrapper name='test6' mode='w' encoding='cp936'> #windows默认cp936(code page),也就是GBK编码Out[112]: <_io.TextIOWrapper name='test6' mode='w' encoding='UTF-8'> #linux默认是UTF-8编码In [4]: f.read() #读取文件In [5]: f.close() #关闭文件
参数:mode 模式
文件操作中,最常见的操作就是读和写。
文件访问的模式有两种:文本模式和二进制模式。不同模式下,操作函数不尽相同,表现的结果也不一样。
下面依次举例介绍每个描述字符意义:
open 默认是只读模式r打开已经存在的文件
在上面的例子中,可以看到默认是文本打开模式,且是只读的。
r模式 (默认只读)In [12]: f = open('c:\test') #默认只读打开In [13]: f.read() #空文件Out[13]: ''In [14]: f.write('mag') #只读模式尝试写入内容返回异常 UnsupportedOperation: not writableIn [15]: f.close() #关闭文件 In [16]: f = open('c:\test','r') # 'r' 模式可写可不写,默认就是只读模式In [17]: f.write('mag') #同样返回异常UnsupportedOperation: not writableIn [18]: f.close() #关闭文件 w模式(只写)In [19]: f = open('c:\test','w') #只写方式打开In [20]: f.read() #不支持读取,异常:UnsupportedOperation: not readableIn [21]: f.write('mag')Out[21]: 3In [22]: f.close() #关闭文件In [13]: !TYPE test #看看内容,ipython下可以使用!TYPE或者!cat 分别调用windows和linux下查看文件内容命令magIn [35]: f.closed #closed属性可以判断文件是否已经关闭,True表示已关闭,Flase未关闭Out[35]: True In [43]: f = open('test1','w') #如果文件不存在,会创建并打开In [45]: f.write('magedu')Out[45]: 6In [46]: f.close() #关闭文件In [47]: !TYPE test1magedu
r
只读打开文件,如果使用write方法,会抛异常 UnsupportedOperation: not writable如果文件不存在,抛出 FileNotFoundError 异常w
表示只写方式打开,如果读取则抛异常 UnsupportedOperation: not readable如果文件不存在,则直接创建文件如果文件存在,则清空文件内容In [48]: f = open('test2','x') #不存在则创建In [49]: f.read() #'x' 模式可写,不可读 异常:UnsupportedOperation: not readableIn [50]: f.write('python')Out[50]: 6In [51]: f.close() #关闭文件 In [52]: f = open('test2','x') #文件已存在抛异常:FileExistsError: [Errno 17] File exists: 'test2'
x
文件不存在,创建文件,并只读方式打开文件存在,抛出异常 FileExistsErrorIn [59]: !TYPE test2pythonIn [60]: f = open('test2','a') #已存在文件In [61]: f.read() #异常 UnsupportedOperation: not readableIn [62]: f.write('\n hello')Out[62]: 5In [63]: f.close() #关闭文件In [64]: !TYPE test2pythonhello In [65]: f = open('test3','a') #文件不存在则创建,不可读,可写In [66]: f.read() #异常UnsupportedOperation: not readableIn [67]: f.write('world')Out[67]: 5In [68]: f.close() #关闭文件In [69]: !TYPE test3world
a
文件存在,只写打开,追加内容文件不存在,则创建后,只写打开,追加内容r是只读,wxa都是只写。
wxa都可以产生新文件,w不管文件存在与否,都会生成全新内容的文件;a不管文件是否存在,都能在打开的文件尾部追加;x必须要求文件事先不存在,自己造一个新文件
文本模式t
字符流,将文件的字节按照某种字符编码理解,按照字符操作。open的默认mode就是rt。
二进制模式b
字节流,将文件就按照字节理解,与字符编码无关。二进制模式操作时,字节操作使用bytes类型In [70]: f = open('test3','rb') #二进制只读In [71]: s = f.read()In [72]: print(type(s))In [73]: print(s)b'world'In [74]: f.close() #关闭文件 In [75]: f = open('test3','wb') #IO对象In [76]: s = f.write('马哥教育'.encode()) #'b' 二进制模式必须使用b'马哥教育'或者encode()转成bytes类型In [77]: print(type(s)) #'int'类型 In [78]: print(s) #返回是什么12In [79]: f.close() #关闭文件In [80]: !TYPE test3椹摜鏁欒偛 #乱码原因后面介绍
In [87]: f = open('test3','x+') #'x' 只能打开不存在文件,否则返回异常 FileExistsErrorIn [88]: f = open('test4','x+')In [89]: f.write('devops')Out[89]: 6In [90]: f.read() #思考:上一步已经写入,为什么读取不到?(缓冲区,需要flush)Out[90]: ''In [91]: f.close()In [92]: !type test4devops
+
为r、w、a、x提供缺失的读写功能,但是,获取文件对象依旧按照r、w、a、x自己的特征。
+不能单独使用,可以认为它是为前面的模式字符做增强功能的。
概念:文件指针
上面的例子中,已经说明了有一个指针。文件指针,指向当前字节位置mode=r,指针起始在0mode=a,指针起始在EOF |
tell()方法 显示指针当前位置
seek(offset[,whence])方法 移动文件指针位置。offset偏移多少字节,whence从哪里开始。文本模式下whence 0 缺省值,表示从头开始,offset只能正整数whence 1 表示从当前位置,offset只接受0whence 2 表示从EOF开始,offset只接受0 |
# 文本模式In [130]: f = open('test5','r+')In [131]: f.tell() # 起始Out[131]: 0In [132]: f.read()Out[132]: 'secopsmetasploit'In [133]: f.tell() # EOFOut[133]: 16In [134]: f.seek(0) #指针移到起始Out[134]: 0In [135]: f.read()Out[135]: 'secopsmetasploit'In [136]: f.seek(2,0)Out[136]: 2In [137]: f.tell()Out[137]: 2In [138]: f.read()Out[138]: 'copsmetasploit'In [139]: f.seek(2,1) #offset只能为0UnsupportedOperation: can't do nonzero cur-relative seeksIn [140]: f.seek(2,2) #offset只能为0UnsupportedOperation: can't do nonzero end-relative seeksIn [141]: f.close()
二进制模式下whence 0 缺省值,表示从头开始,offset只能正整数whence 1 表示从当前位置,offset可正可负whence 2 表示从EOF开始,offset可正可负EOF(end of file,即文件结尾,终点) |
#二进制模式In [111]: f = open('test5','r+b')In [112]: f.tell() #起始Out[112]: 0In [113]: f.read() #读取完指针移到EOFOut[113]: b'secops'In [114]: f.tell() #EOFOut[114]: 6In [115]: f.write(b'metasploit')Out[115]: 10In [116]: f.tell()Out[116]: 16In [117]: f.seek(0) #移到起始Out[117]: 0In [118]: f.seek(2,1) #当前位置向后偏移2位Out[118]: 2In [119]: f.read()Out[119]: b'copsmetasploit'In [120]: f.seek(-2,1) #当前位置向前偏移2位Out[120]: 14In [121]: f.seek(-3,1)Out[121]: 11In [122]: f.seek(2,2) #从EOF开始Out[122]: 18In [123]: f.seek(0)Out[123]: 0In [125]: f.seek(-2,1) #异常,向前偏移不可越界OSError: [Errno 22] Invalid argumentIn [126]: f.tell()Out[126]: 0In [127]: f.read()Out[127]: b'secopsmetasploit'In [128]: f.seek(-20,2) #异常,向前偏移不可越界OSError: [Errno 22] Invalid argumentIn [129]: f.close()
二进制模式支持任意起点的偏移,从头、从尾、从中间位置开始。
向后seek可以超界,但是向前seek的时候,不能超界,否则抛异常。
参数:buffering 缓冲区
-1 表示使用缺省大小的buffer。如果是二进制模式,使用 io.DEFAULT_BUFFER_SIZE 值(缺省缓冲区大小,字节),默认是4096或者8192 字节。如果是文本模式,或者终端设备,是行缓存模式,如果不是,则使用二进制模式的策略。0 只在二进制模式使用,表示关buffer1 只在文本模式使用,表示使用行缓冲。意思就是见到换行符就flush大于1 用与指定buffer的大小,如果不指定或者负数,表示使用缺省大小 |
flush() 将缓冲区数据写入磁盘close() 关闭前会调用flush()
buffer 缓冲区
缓冲区一个内存空间,一般来说是一个FIFO队列(file input file output),到缓冲区满了或者达到阈值,数据才会flush到磁盘In [142]: import ioIn [143]: io.DEFAULT_BUFFER_SIZE #系统默认buffer大小Out[143]: 8192
buffering 说明buffering=-1 t和b,都是 io.DEFAULT_BUFFER_SIZE 大小buffering=0 b关闭缓冲区t不支持 (文件模式简写t)buffering=1 b就1个字节t行缓冲,遇到换行符才flushbuffering>1 b模式表示行缓冲大小。缓冲区的值可以超过 io.DEFAULT_BUFFER_SIZE,直到设定的值超过后才把缓冲区flusht模式,是 io.DEFAULT_BUFFER_SIZE,flush完后把当前字符串也写入磁盘 |
似乎看起来很麻烦,一般来说,只需要记得:
文本模式,一般都用默认缓冲区大小二进制模式,是一个个字节的操作,可以指定buffer的大小一般来说,默认缓冲区大小是个比较好的选择,除非明确知道,否则不调整它一般编程中,明确知道需要写入硬盘了,都会手动调用依次flush,而不是等到自动flush或者close的时候参数:encoding 编码,仅文本模式使用
None 表示使用缺省编码,依赖操作系统。win、linux下测试如下代码
In [148]: f = open('test6','w')In [149]: fOut[149]: <_io.TextIOWrapper name='test6' mode='w' encoding='cp936'> #linux下是UTF-8In [150]: f.write('啊')Out[150]: 1In [151]: f.close()
其它参数:
errors:什么样的编码错误将被捕获None和strict表示有编码错误将抛出ValueError异常;ignore表示忽略newline:文本模式中,换行的转换。可以为None、''、'\r'、'\n'、'\r\n'
读时,None表示 '\r'、'\n'、'\r\n' 都将转换为'\n';'' 表示不会不会自动转换通用换行符;其它合法字符表示换行符就是指定字符,就会按照指定字符分行写时,None表示'\n'(win)或'\r\n'(linux)都会被替换为系统缺省行分割符os.linesep;'\n'或''表示'\n'不转换;其它合法字符表示'\n'(win)或'\r\n'(linux)会被替换为指定的字符In [155]: import osIn [156]: os.linesepOut[156]: ' '
read方法
read(size=-1)
size 表示读取的多少个字符或字节;负数或者None表示读到EOF
readline方法 行读取
readline(size=-1)
一行行读取文件内容。size设置依次能够读取行内几个字符或者字节。readlines(hin=-1)读取所有行的列表。指定hint则返回指定的行数。
close方法
flush并关闭文件对象。
文件已经关闭,再次关闭没有任何效果。 其他方法seekable() 是否可seekreadable() 是否可读write() 是否可写closed 是否已经关闭
上下文管理
问题的引出
在linux中,执行(3.5.3/envs/magedu353) [python@ihoneysec cmdb]$ touch test(3.5.3/envs/magedu353) [python@ihoneysec cmdb]$ vim context.py #写入以下代码lst = []for _ in range(2000):lst.append(open('test'))print(len(lst)) (3.5.3/envs/magedu353) [python@ihoneysec cmdb]$ python context.pyTraceback (most recent call last):File "context.py", line 3, inOSError: [Errno 24] Too many open files: 'test' (3.5.3/envs/magedu353) [python@ihoneysec cmdb]$ lsof|wc -l1068
lsof 列出打开的文件(list open files)$ lsof |grep test|wc -lulimit -a查看所有限制。其中open files就是打开文件数的限制,默认1024$ ulimit -n 65535 #可以修改为65535,但要根据硬件资源调整为合适的大小
for x in lst: zx.close()
将文件依次关闭,然后就可以继续打开了。再看一次lsof。
如何解决?
1、异常处理当出现异常的时候,拦截异常。但是,因为很多代码都可能出现OSError异常;还不好判断异常就时因为资源限制产生的f = open('test')try: f.write('abc') #文件只读,写入失败finally: f.close() #finally 可以保证打开的文件可以被关闭。
2、上下文管理一种特殊的语法,交给解释器取释放文件对象
with open('test6') as f: f.write('abc') f.closed #测试f是否关闭
上下文管理
使用with..as 关键字上下文管理的语句块并不会开启新的作用域with语句块执行完的时候,会自动关闭文件对象
对于类似于文件对象的IO对象,一般来说都需要在不使用的时候关闭、注销,以释放资源。
IO被打开的时候,会获得一个文件描述符。计算机资源是有限的,所有操作系统都会做限制。就是为了保护计算机的资源不要被完全耗尽,计算机资源是共享的,不是独占的。一般情况下,除非特别明确的知道资源情况,否则不要提高资源的限制值来解决问题。