Python 文件操作
文件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]: f Out[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 writable In [15]: f.close() #关闭文件 In [16]: f = open('c:\test','r') # 'r' 模式可写可不写,默认就是只读模式 In [17]: f.write('mag') #同样返回异常UnsupportedOperation: not writable In [18]: f.close() #关闭文件 w模式(只写) In [19]: f = open('c:\test','w') #只写方式打开 In [20]: f.read() #不支持读取,异常:UnsupportedOperation: not readable In [21]: f.write('mag') Out[21]: 3 In [22]: f.close() #关闭文件 In [13]: !TYPE test #看看内容,ipython下可以使用!TYPE或者!cat 分别调用windows和linux下查看文件内容命令 mag In [35]: f.closed #closed属性可以判断文件是否已经关闭,True表示已关闭,Flase未关闭 Out[35]: True In [43]: f = open('test1','w') #如果文件不存在,会创建并打开 In [45]: f.write('magedu') Out[45]: 6 In [46]: f.close() #关闭文件 In [47]: !TYPE test1 magedu
r
只读打开文件,如果使用write方法,会抛异常 UnsupportedOperation: not writable
如果文件不存在,抛出 FileNotFoundError 异常
w
表示只写方式打开,如果读取则抛异常 UnsupportedOperation: not readable
如果文件不存在,则直接创建文件
如果文件存在,则清空文件内容
In [48]: f = open('test2','x') #不存在则创建 In [49]: f.read() #'x' 模式可写,不可读 异常:UnsupportedOperation: not readable In [50]: f.write('python') Out[50]: 6 In [51]: f.close() #关闭文件 In [52]: f = open('test2','x') #文件已存在抛异常:FileExistsError: [Errno 17] File exists: 'test2'
x
文件不存在,创建文件,并只读方式打开
文件存在,抛出异常 FileExistsError
In [59]: !TYPE test2 python In [60]: f = open('test2','a') #已存在文件 In [61]: f.read() #异常 UnsupportedOperation: not readable In [62]: f.write('\n hello') Out[62]: 5 In [63]: f.close() #关闭文件 In [64]: !TYPE test2 python hello In [65]: f = open('test3','a') #文件不存在则创建,不可读,可写 In [66]: f.read() #异常UnsupportedOperation: not readable In [67]: f.write('world') Out[67]: 5 In [68]: f.close() #关闭文件 In [69]: !TYPE test3 world
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)) <class 'bytes'> 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'类型 <class 'int'> In [78]: print(s) #返回是什么 12 In [79]: f.close() #关闭文件 In [80]: !TYPE test3 椹摜鏁欒偛 #乱码原因后面介绍
In [87]: f = open('test3','x+') #'x' 只能打开不存在文件,否则返回异常 FileExistsError In [88]: f = open('test4','x+') In [89]: f.write('devops') Out[89]: 6 In [90]: f.read() #思考:上一步已经写入,为什么读取不到?(缓冲区,需要flush) Out[90]: '' In [91]: f.close() In [92]: !type test4 devops
+
为r、w、a、x提供缺失的读写功能,但是,获取文件对象依旧按照r、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 |
# 文本模式 In [130]: f = open('test5','r+') In [131]: f.tell() # 起始 Out[131]: 0 In [132]: f.read() Out[132]: 'secopsmetasploit' In [133]: f.tell() # EOF Out[133]: 16 In [134]: f.seek(0) #指针移到起始 Out[134]: 0 In [135]: f.read() Out[135]: 'secopsmetasploit' In [136]: f.seek(2,0) Out[136]: 2 In [137]: f.tell() Out[137]: 2 In [138]: f.read() Out[138]: 'copsmetasploit' In [139]: f.seek(2,1) #offset只能为0 UnsupportedOperation: can't do nonzero cur-relative seeks In [140]: f.seek(2,2) #offset只能为0 UnsupportedOperation: can't do nonzero end-relative seeks In [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]: 0 In [113]: f.read() #读取完指针移到EOF Out[113]: b'secops' In [114]: f.tell() #EOF Out[114]: 6 In [115]: f.write(b'metasploit') Out[115]: 10 In [116]: f.tell() Out[116]: 16 In [117]: f.seek(0) #移到起始 Out[117]: 0 In [118]: f.seek(2,1) #当前位置向后偏移2位 Out[118]: 2 In [119]: f.read() Out[119]: b'copsmetasploit' In [120]: f.seek(-2,1) #当前位置向前偏移2位 Out[120]: 14 In [121]: f.seek(-3,1) Out[121]: 11 In [122]: f.seek(2,2) #从EOF开始 Out[122]: 18 In [123]: f.seek(0) Out[123]: 0 In [125]: f.seek(-2,1) #异常,向前偏移不可越界 OSError: [Errno 22] Invalid argument In [126]: f.tell() Out[126]: 0 In [127]: f.read() Out[127]: b'secopsmetasploit' In [128]: f.seek(-20,2) #异常,向前偏移不可越界 OSError: [Errno 22] Invalid argument In [129]: f.close()
二进制模式支持任意起点的偏移,从头、从尾、从中间位置开始。
向后seek可以超界,但是向前seek的时候,不能超界,否则抛异常。
参数:buffering 缓冲区
-1 表示使用缺省大小的buffer。 如果是二进制模式,使用 io.DEFAULT_BUFFER_SIZE 值(缺省缓冲区大小,字节),默认是4096或者8192 字节。 如果是文本模式,或者终端设备,是行缓存模式,如果不是,则使用二进制模式的策略。 0 只在二进制模式使用,表示关buffer 1 只在文本模式使用,表示使用行缓冲。意思就是见到换行符就flush 大于1 用与指定buffer的大小,如果不指定或者负数,表示使用缺省大小 |
flush() 将缓冲区数据写入磁盘
close() 关闭前会调用flush()
buffer 缓冲区
缓冲区一个内存空间,一般来说是一个FIFO队列(file input file output),到缓冲区满了或者达到阈值,数据才会flush到磁盘
In [142]: import io In [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行缓冲,遇到换行符才flush buffering>1 b模式表示行缓冲大小。缓冲区的值可以超过 io.DEFAULT_BUFFER_SIZE,直到设定的值超过后才把缓冲区flush t模式,是 io.DEFAULT_BUFFER_SIZE,flush完后把当前字符串也写入磁盘 |
似乎看起来很麻烦,一般来说,只需要记得:
文本模式,一般都用默认缓冲区大小
二进制模式,是一个个字节的操作,可以指定buffer的大小
一般来说,默认缓冲区大小是个比较好的选择,除非明确知道,否则不调整它
一般编程中,明确知道需要写入硬盘了,都会手动调用依次flush,而不是等到自动flush或者close的时候
参数:encoding 编码,仅文本模式使用
None 表示使用缺省编码,依赖操作系统。win、linux下测试如下代码
In [148]: f = open('test6','w') In [149]: f Out[149]: <_io.TextIOWrapper name='test6' mode='w' encoding='cp936'> #linux下是UTF-8 In [150]: f.write('啊') Out[150]: 1 In [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 os In [156]: os.linesep Out[156]: ' '
closefd:关闭文件描述符,True表示关闭它。False会在文件关闭后保持这个描述符。
fileobj.fineno()查看
read方法
read(size=-1)
size 表示读取的多少个字符或字节;负数或者None表示读到EOF
readline方法 行读取
readline(size=-1)
一行行读取文件内容。size设置依次能够读取行内几个字符或者字节。
readlines(hin=-1)
读取所有行的列表。指定hint则返回指定的行数。
close方法
flush并关闭文件对象。
文件已经关闭,再次关闭没有任何效果。
其他方法
seekable() 是否可seek
readable() 是否可读
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.py Traceback (most recent call last): File "context.py", line 3, in <module> OSError: [Errno 24] Too many open files: 'test'
(3.5.3/envs/magedu353) [python@ihoneysec cmdb]$ lsof|wc -l 1068
lsof 列出打开的文件(list open files)
$ lsof |grep test|wc -l
ulimit -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被打开的时候,会获得一个文件描述符。计算机资源是有限的,所有操作系统都会做限制。
就是为了保护计算机的资源不要被完全耗尽,计算机资源是共享的,不是独占的。
一般情况下,除非特别明确的知道资源情况,否则不要提高资源的限制值来解决问题。