文件IO
目录
open方法:... 1
文件指针:... 3
上下文件管理:... 14
1、异常处理:... 15
2、上下文管理:... 15
习题:... 17
冯诺依曼体系架构:
CPU:运算器和控制器组成;
运算器,完成各种算数运算、逻辑运算、数据传输等数据加工处理;
控制器,控制程序的执行;
存储器,用于记忆程序和数据,如内存;
输入设备,将数据或程序输入到计算机中,如keyboard、mouse;
输出设备,将数据或程序的处理结果展示给用户,如monitor、printer等;
一般说IO操作,指的是文件IO;如果指的是网络IO,都会直接说网络IO;
linux|unix中,一切皆文件,包括设备;
可操作字符设备,可当作文件,socket文件,pipe文件等;
文件io常用操作:
open
read
write
close
readline,行读取;
readlines,多行读取;
seek,文件指针操作;
open方法:
open(file,mode='r',buffering=-1,encoding=None,errors=None,newline=None,closefd=True,opener=None)
打开一个文件,返回一个文件对象(流对象)和文件描述符;打开文件失败,则返回异常;
file,,打开或要创建的文件名,如果不指定路径,默认是当前路径,file也可以是一个文件描述符;
mode,模式:
r,缺省的,表示只读打开;只读打开文件,如果使用write方法,抛异常UnsupportedOperation: not writable;如果文件不存在,抛异常FileNotFoundError;
w,只写打开;只写方式打开,如果读取则抛异常UnsupportedOperation: not readable;如果文件不存在,则直接创建文件;如果文件存在,则清空文件内容;
x,创建并写入一个新文件;文件不存在,创建文件并只写方式打开;文件存在,抛FileExistsError异常;
a,写入打开,如果文件存在,则追加;文件存在,只写打开,追加内容;文件不存在,则创建后,只写打开,追加内容;
b,二进制模式;字节流,将文件按照字节理解,与字符编码无关,二进制格式操作时,字节操作使用bytes类型;
t,缺省的,文本模式;字符流,将文件的字节按某种字符编码理解,按字符操作,open的默认模式就是rt;
+,读写打开一个文件,给原来只读、只写方式打开提供缺失的写或者读能力(缺什么补什么);为rwax提供缺失的读写功能,但获取文件对象依旧按照rwax自己的特征,+不能单独使用,可认为它是前面的模式字符做增强功能的;
注:
文件操作中,最常用的操作就是读和写;
文件访问的模式有两种,文本模式和二进制模式,不同模式,操作函数不尽相同,表现的结果也不一样;
r是只读,wxa都是只写;
wxa都可产生新文件;
w,不管文件存在与否,都会生成全新内容的文件;
a,不管文件是否存在,都能在打开的文件尾部追加;
x,必须要求文件事先不存在,自己造一个文件;
buffering缓冲区:
缓冲区,一个内存空间,一般来说是一个FIFO队列,缓冲区满了或达到阈值,数据都会flush到磁盘;
-1,表示使用缺省大小的buffer,t和b模式都是io.DEFAULT_BUFFER_SIZE;
0,只在b二进制模式使用,表示关闭缓冲区;t不支持;
1,只在t文本模式使用,表示使用行缓冲,即见到换行符就flush();b就一个字节;
>1,用于指定buffer的大小,t是io.DEFAULT_BUFFER_SIZE,flush()完后会把当前字符串写入磁盘;b,表示行缓冲大小,缓冲区的值可以超过io.DEFAULT_BUFFER_SIZE,直到设定的值超出后才把缓冲区flush();
注:
二进制模式b,一个个字节操作,可指定buffer大小,使用io.DEFAULT_BUFFER_SIZE值,默认是4096或8192,单位byte;
文本模式t,一般用默认缓冲区大小;如果是终端设备,是行缓存方式;如果不是终端设备,则使用二进制模式的策略;
tb两种模式下,使用buffering情况下,只要用过seek()和tell(),就会flush();
flush(),将缓冲区数据写入磁盘;
close(),关闭前会调用flush();
In [2]: import io
In [3]: io.DEFAULT_BUFFER_SIZE #缺省缓冲区大小,单位字节
Out[3]: 8192
对c、c++来说,缓冲区溢出是一种***手段(很多函数不作边界检查);在有边界检查的语言中,这种手段用不了;
一般来说,默认缓冲区大小是个比较好的选择,除非明确知道,否则不调整它;
一般编程中,明确知道需要写磁盘了,都会手动调用一次flush(),而不是等到自动flush或close的时候;
encoding,编码,仅t下使用:
None,表示使用缺省编码,依赖OS;
其它参数:
errors:
什么样的编码错误将被捕获;
None和Strict一样,表示有编码错误抛ValueError异常;
ignore,忽略;
newline:
文本模式中,换行的转换,可以为None,''、\r、\n、\r\n;
读时,None表示\r、\n、\r\n都被转换为\n;''表示不会自动转换通用换行符;其它合法字符表示换行符就是指定字符,就会按指定字符分行;
写时,None表示\n都被替换为系统缺省行分隔符,os.linesep;\n或''表示\n不替换;其它合法字符表示\n会被替换为指定的字符;
closefd:
fd,file descriptor,关闭文件描述符;
True表示关闭它;
False会在文件关闭后保持这个描述符;
用fileobj.fileno()查看,如f.fileno(),读(共用文件描述符);f.close()后,fd会释放;每次打开fd往后加,之前的fd,OS有缓存,再次打开时可能会和之前的一样;
文件指针:
指向当前字节位置;
mode=r,指针起始在0;
mode=a,指针起始在EOF;
tell(),显示指针当前位置;
seek(offset[,whence])
移动文件指针位置,相对whence走;
offset,偏移多少字节,whence从哪里开始;
seek是跳字节,中文'啊',utf-8要跳3个byte,cp936要跳2个字节;
在中文下用seek不好,一般在英文下用seek,一般跳至尾部;
一般头开始,或按换行一行行读,不是所有文件都可seek;
文本模式下,支持从开头向后偏移的方式:
whence为0,缺省值,表示从头开始,offset只能正整数;
whence为1,表示从当前位置,offset只接受0;
whence为2,表示从EOF开始,offset只接受0;
f.seek(0,1) #没意义,原地不动;
f.seek(0,2) #跳至末尾;
二进制模式下:
whence为0,缺省值,表示从头开始,offset只能正整数;
whence为1,表示从当前位置,offset可正负数;
whence为2,表示从EOF开始,offset可正负数;
二进制模式支持任意起点的偏移,从头,从尾,从中间位置均可;
向后seek可以超界,但向前seek时不能超界,否则抛异常;
例:
In [36]: f=open('test','x')
In [37]: f #linux下,编码为UTF-8;win下,编码为cp936,类似GBK
Out[37]: <_io.TextIOWrapper name='test' mode='x' encoding='UTF-8'>
In [38]: f.close() #有开有关,不可以只开不关
注:
简体中文:
GB2312,子集;
GBK,真超集,完全包括GB2312(是GB2312的超集),双字节编码(不是真正的编码),扩充了CJK(japanese,korea);
ANSI,即GBK;
cp936,code page代码页,等价于GBK,是映射到GBK上的,但绝不是UTF-8;
GBK<->UTF-8,任意一个如果少某个字则对应不起来,会乱码;
需搞定:ascii、GBK、GB2312;
要保证源与目标的编码一致;
半角,ascii字符;
ascii的0-127个字符对所有编码都通用;
文件放在disk上,一般很少修改;
read()方法,读到最后读不出来,不会报错;
注:
notepad++上,显示所有字符按钮,可看到换行符;
dos/windows,换行符为\r\n,上图CRLF;ANSI,即GBK;
unix,换行符为\n,上图LF,UTF-8;
编辑->文档格式转换->转为UNIX格式;
编码->转为UTF-8无BOM编码格式 ;
例:
In [40]: f=open('test')
In [41]: f
Out[41]: <_io.TextIOWrapper name='test' mode='r' encoding='UTF-8'>
In [42]: f.read()
Out[42]: ''
In [43]: f.write('abc') #只读打开文件,如果使用write方法,抛异常
---------------------------------------------------------------------------
UnsupportedOperation Traceback (most recent call last)
<ipython-input-43-3626d6c87fee> in <module>()
----> 1 f.write('abc')
UnsupportedOperation: not writable
In [44]: f.close()
例:
In [46]: f=open('test','w') #同f=open('test',mode='w')
In [47]: f.write('中')
Out[47]: 1
In [48]: f.close()
In [49]: f=open('test')
In [50]: f.read()
Out[50]: '中'
In [51]: f.close()
In [52]: f=open('test','w')
In [53]: f.read() #只写方式打开,如果读取则抛异常;如果文件不存在,则直接创建文件;如果文件存在,则清空文件内容;
---------------------------------------------------------------------------
UnsupportedOperation Traceback (most recent call last)
<ipython-input-53-571e9fb02258> in <module>()
----> 1 f.read()
UnsupportedOperation: not readable
In [54]: f.close()
例:
In [55]: f=open('test','x') #文件不存在,创建文件并只写方式打开;文件存在,抛FileExistsError异常
---------------------------------------------------------------------------
FileExistsError Traceback (most recent call last)
<ipython-input-55-50ca7657a95a> in <module>()
----> 1 f=open('test','x')
FileExistsError: [Errno 17] File exists: 'test'
例:
In [56]: f=open('test','a') #文件存在,只写打开,追加内容;文件不存在,则创建后,只写打开,追加内容;
In [57]: f.read()
---------------------------------------------------------------------------
UnsupportedOperation Traceback (most recent call last)
<ipython-input-57-571e9fb02258> in <module>()
----> 1 f.read()
UnsupportedOperation: not readable
In [58]: f.write('def')
Out[58]: 3
In [59]: f.close()
例:
In [61]: f=open('test','rb')
In [62]: s=f.read()
In [63]: type(s)
Out[63]: bytes
In [64]: s
Out[64]: b'def'
In [65]: f.close()
In [66]: f=open('test','wb')
In [67]: f.write(b'abc') #同f.write('abc'.encode())
Out[67]: 3
In [68]: f.write('abc'.encode()) #第二次及之后的写即追加
Out[68]: 3
In [69]: f.write('马哥'.encode())
Out[69]: 6
In [70]: f.close()
In [71]: cat test
abcabc马哥
In [72]: f=open('test') #默认rt,按字符理解
In [73]: f.read(3)
Out[73]: 'abc'
In [74]: f.read(3)
Out[74]: 'abc'
In [75]: f.read(1)
Out[75]: '马'
In [76]: f.read(1)
Out[76]: '哥'
In [77]: f.read(1)
Out[77]: ''
In [78]: f.close()
例:
In [79]: f=open('test','rb') #按字节理解,一个字节一个字节的读
In [80]: f
Out[80]: <_io.BufferedReader name='test'>
In [81]: f.read(3)
Out[81]: b'abc'
In [82]: f.read(3)
Out[82]: b'abc'
In [83]: f.read(1) #中文,则一个字节一个字节的读
Out[83]: b'\xe9'
In [84]: f.read(1)
Out[84]: b'\xa9'
In [85]: f.read(1)
Out[85]: b'\xac'
In [86]: f.read(1)
Out[86]: b'\xe5'
In [87]: f.read(1)
Out[87]: b'\x93'
In [88]: f.read(1)
Out[88]: b'\xa5'
In [89]: f.read(1)
Out[89]: b''
In [90]: f.read(3)
Out[90]: b''
例:
In [91]: f=open('test','r+')
In [92]: f.read()
Out[92]: 'abcabc马哥'
In [93]: f.write('abc')
Out[93]: 3
In [94]: f.read() #读不到,指针在文件末尾
Out[94]: ''
In [95]: f.close()
In [96]: cat test
abcabc马哥abc
例:
In [97]: f=open('test','w+')
In [98]: f.read()
Out[98]: ''
In [99]: f.close()
In [100]: cat test
例:
In [101]: f=open('test','a+')
In [102]: f.write('abc123')
Out[102]: 6
In [103]: f.read() #指针在末尾,读不到
Out[103]: ''
In [104]: f.close()
In [105]: cat test
abc123
In [106]: f=open('test','a+')
In [107]: f.read()
Out[107]: ''
In [108]: f.write('def')
Out[108]: 3
In [109]: f.close()
In [110]: cat test
abc123def
例:
In [111]: f=open('test')
In [112]: f.tell()
Out[112]: 0
In [113]: f.read(1)
Out[113]: 'a'
In [114]: f.tell()
Out[114]: 1
In [115]: f.read(1)
Out[115]: 'b'
In [116]: f.tell()
Out[116]: 2
In [117]: f.close()
In [1]: f=open('test','r+b')
In [2]: f.read(1)
Out[2]: b'a'
In [3]: f.write(b'xyz')
Out[3]: 3
In [4]: f.read()
Out[4]: b'23def'
In [5]: f.tell()
Out[5]: 9
In [6]: f.seek(0)
Out[6]: 0
In [7]: f.read()
Out[7]: b'axyz23def'
In [8]: f.close()
In [9]: f=open('test','a+b')
In [10]: f.tell()
Out[10]: 9
In [11]: f.write('啊') #二进制模式下,f.write('啊'),f.write(b'啊')都是错的
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-11-1d73ad7bd8da> in <module>()
----> 1 f.write('啊')
TypeError: a bytes-like object is required, not 'str'
In [12]: f.write('啊'.encode())
Out[12]: 3
In [13]: f.read() #指针到末尾
Out[13]: b''
In [14]: f.seek(0) #指针到开头
Out[14]: 0
In [15]: f.read()
Out[15]: b'axyz23def\xe5\x95\x8a'
例:
In [18]: f=open('test')
In [19]: f.tell()
Out[19]: 0
In [20]: f.seek(2)
Out[20]: 2
In [21]: f.tell()
Out[21]: 2
In [22]: f.read(1)
Out[22]: 'y'
In [23]: f.seek(1,1) #文本模式下,whence为1或2,offset不支持非0
---------------------------------------------------------------------------
UnsupportedOperation Traceback (most recent call last)
<ipython-input-23-39165bc2c97f> in <module>()
----> 1 f.seek(1,1)
UnsupportedOperation: can't do nonzero cur-relative seeks
In [24]: f.seek(1,0)
Out[24]: 1
In [25]: f.tell()
Out[25]: 1
In [26]: f.read(1)
Out[26]: 'x'
In [27]: f.close()
例:
In [28]: f=open('test','r+b')
In [29]: f.tell()
Out[29]: 0
In [30]: f.seek(2)
Out[30]: 2
In [31]: f.read(1)
Out[31]: b'y'
In [32]: f.seek(1,1) #二进制模式下,whence可用1、2
Out[32]: 4
In [33]: f.tell()
Out[33]: 4
In [34]: f.read(1)
Out[34]: b'2'
In [35]: f.seek(1,2)
Out[35]: 13
In [36]: f.read(1)
Out[36]: b''
In [37]: f.tell()
Out[37]: 13
In [38]: f.seek(-2,2) #seek是跳字节,中文'啊',utf-8要跳3个byte,cp936要跳2个字节
Out[38]: 10
In [39]: f.read()
Out[39]: b'\x95\x8a'
In [40]: f.close()
In [41]: f=open('test','r+b')
In [42]: f.read()
Out[42]: b'axyz23def\xe5\x95\x8a'
In [43]: f.seek(-5,2)
Out[43]: 7
In [44]: f.seek(-50,2)
---------------------------------------------------------------------------
OSError Traceback (most recent call last)
<ipython-input-44-1d1cfde8ca97> in <module>()
----> 1 f.seek(-50,2)
OSError: [Errno 22] Invalid argument
In [45]: f.tell()
Out[45]: 7
In [46]: f.seek(-1,0)
---------------------------------------------------------------------------
OSError Traceback (most recent call last)
<ipython-input-46-e0452c658131> in <module>()
----> 1 f.seek(-1,0)
OSError: [Errno 22] Invalid argument
In [47]: f.seek(2,1)
Out[47]: 9
In [48]: f.read()
Out[48]: b'\xe5\x95\x8a'
In [49]: f.close()
例:
In [5]: f=open('test','w+b')
In [6]: f.write(b'abc'*512)
Out[6]: 1536
In [7]: f.write(b'abc'*512)
Out[7]: 1536
In [8]: f.write(b'abc'*512)
Out[8]: 1536
In [9]: f.write(b'abc'*512)
Out[9]: 1536
In [10]: f.write(b'abc'*512)
Out[10]: 1536
In [11]: f.write(b'abc'*512)
Out[11]: 1536
In [12]: f.close()
In [13]: f=open('test','w+b')
In [14]: f.write(b'abcdefg'*512)
Out[14]: 3584
In [15]: f.write(b'hijklmn') #write有自己的策略,不知道什么时候会写入到磁盘
Out[15]: 7
In [16]: f.write(b'opqrstuvwxyz')
Out[16]: 12
In [17]: f.flush()
In [18]: f.close()
In [23]: f=open('test','w+b',buffering=0) #效率低,很少用
In [24]: f.write(b'abcd') #实时写入到磁盘
Out[24]: 4
In [25]: f.write(b'efgh')
Out[25]: 4
In [26]: f.seek(0) #二进制模式在不用buffer的情况下,可seek()
Out[26]: 0
In [27]: f.tell()
Out[27]: 0
In [28]: cat test
abcdefgh
In [29]: f.close()
In [30]: f=open('test','w+',buffering=1) #文本模式下,行缓冲,见到换行符就flush()
In [31]: f.write('abcefg') #cat test
Out[31]: 6
In [32]: f.write('\nhijklmn') #cat test
Out[32]: 8
In [33]: f.close()
In [34]: f=open('test','w+b',buffering=16) #凑够16个字节就flush()
In [35]: f.write(b'abcdefghijklmno')
Out[35]: 15
In [36]: f.write(b'p') #cat test
Out[36]: 1
In [37]: f.write(b'q') #cat test,已写入
Out[37]: 1
In [38]: f.close()
In [39]: f=open('test','w',buffering=16) #文本模式下,使用的是默认8192
In [40]: f.write('abcdefghijklmnop')
Out[40]: 16
In [41]: f.write('q') #cat test,没有flush()
Out[41]: 1
In [42]: f.close()
注:
tb两种模式下,使用buffering情况下,只要用过seek()和tell(),就会flush();
例:
将linux上test文件(内容'啊')拷至win下,用ipython打开;
在win下新建test文件,用notepad++打开;
f.read(size=-1)
size表示读取的多少个字符或字节,负数或None表示读取到EOF;
行读取:
f.readline(size=-1)
一行行读取文件内容,size设置表示一次能读取行内几个字符或字节;
f.readlines(hint=-1)-->list
读取所有行,返回新列表,hint设置表示返回指定的行数;
f.write(s)
把字符串s写入到文件中并返回字符的个数;
注:
f.write(b'abc')同f.write('abc'.encode())
f.close()
flush()并关闭文件对象,文件若已关闭,再次关闭没有任何效果;
其它:
seekable(),是否可seek;
readable(),是否可读;
writable(),是否可写;
closed,是否已经关闭,如f.closed;
上下文件管理:
例:
问题引出:
In [49]: lst = []
In [50]: for _ in range(1024):
...: lst.append(open('test'))
...:
---------------------------------------------------------------------------
OSError Traceback (most recent call last)
<ipython-input-50-088959e92295> in <module>()
1 for _ in range(1024):
----> 2 lst.append(open('test'))
3
OSError: [Errno 24] Too many open files: 'test'
In [51]: len(lst)
Out[51]: 1012
In [52]: The history saving thread hit an unexpected error (OperationalError('unable to open database file',)).History will not be written to the database.
]$ lsof -p 17267 | wc -l #list open files
1076
]$ ulimit -n
1024
In [52]: for x in lst:
...: x.close()
...:
]$ lsof -p 17267 | wc -l
65
解决:
1、异常处理:
当异常出现时,拦截异常,但因为很多代码都可能出现OSError异常,不好判断异常原因就是资源限制产生的;
In [53]: f=open('test')
In [54]: try:
...: f.write('abc') #文件只读打开,写入失败,并不是资源限制ulimit -n问题
...: finally:
...: f.close()
...:
---------------------------------------------------------------------------
UnsupportedOperation Traceback (most recent call last)
<ipython-input-54-4ae821d67aaf> in <module>()
1 try:
----> 2 f.write('abc')
3 finally:
4 f.close() #这样可以,finally可保证打开的文件可以被关闭
5
UnsupportedOperation: not writable
In [55]: f.closed
Out[55]: True
2、上下文管理:
使用with... as...关键字;
上下文管理的语句块并不会开启新的作用域;
with语句块执行完的时候,会自动关闭文件对象;
例:
In [56]: with open('test') as f:
...: f.read()
...: print(f.closed)
...: raise ValueError()
...:
False
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-56-5671bab184ba> in <module>()
2 f.read()
3 print(f.closed)
----> 4 raise ValueError()
ValueError:
In [57]: f.closed
Out[57]: True
例:
In [58]: with open('test') as f2:
...: f2.write('abc')
...:
---------------------------------------------------------------------------
UnsupportedOperation Traceback (most recent call last)
<ipython-input-58-6a559f2b5f65> in <module>()
1 with open('test') as f2:
----> 2 f2.write('abc')
3
UnsupportedOperation: not writable
In [59]: f.closed
Out[59]: True
例:
In [60]: f3 = open('test')
In [61]: with f3: #另一种写法,很少这么写
...: pass
...:
In [62]: f3.closed
Out[62]: True
注:
对于类似文件的IO对象,一般来说都需要在不使用的时候关闭,注销,以释放资源;
io被打开的时候,会获得一个fd,计算机资源是有限的,所以OS会做限制,就是为了保护计算机的资源不被完全耗尽,计算机资源是共享的,不是独占的;
一般情况下,除非特别明确知道资源情况,否则不要提高资源的限制值来解决问题;
习题:
1、文件复制?
2、对一个文件,进行单词统计,不区分大小写,并显示单词重复最多的10个单词(top10)?
1、
with open('e:/update/test.txt') as f:
with open('e:/update/test_copy.txt','w') as f2:
s1 = f.read()
f2.write(s1)
2、
例:
def wordcount(file='e:/update/sample.txt'):
chars = '''~!@#$%^&*()_+{}[]/\\/"'=;:.<>'''
with open(file,encoding='utf-8') as f:
word_count = {}
for line in f: #逐行取
words = line.split()
for k,v in zip(words,(1,)*len(words)): #每个单词为k,v为1
k = k.strip(chars)
k = k.lower()
word_count[k] = word_count.get(k,0) + 1
lst = sorted(word_count.items(),key=lambda x:x[1],reverse=True) #用元组的第二个元素排序,排序应是全局的,编程中要注意,可动态变化的显示
for i in range(10):
print(str(lst[i]).strip("'()").replace("'",""))
return lst
print(wordcount())
例:
def wordcount(file='e:/update/sample.txt'):
chars = '''~!@#$%^&*()_+{}[]/\\/"'=;:.<>'''
charset = set(chars)
with open(file,encoding='utf-8') as f:
word_count = {}
for line in f:
words = line.split()
for k,v in zip(words,(1,)*len(words)):
k = k.strip(chars)
if len(k) < 1:
continue
k = k.lower()
start = 0
for i,c in enumerate(k):
if c in charset:
# print(k[start:i],start,i)
if start == i:
start = i + 1
continue
key = k[start:i]
word_count[key] = word_count.get(k,0) + 1
start = i + 1
key = k[start:len(k)]
word_count[key] = word_count.get(key, 0) + 1
else:
# print('---',k[start:])
key = k[start:]
word_count[key] = word_count.get(k,0) + 1
# print()
lst = sorted(word_count.items(),key=lambda x:x[1],reverse=True)
for i in range(10):
if i < len(lst):
print(str(lst[i]).strip("'()").replace("'",""))
return lst
print(wordcount())
注:
lambda x: x
同
def fn(x):
return x
转载于:https://blog.51cto.com/jowin/2388720