文件IO

目录

open方法:... 1

文件指针:... 3

上下文件管理:... 14

1、异常处理:... 15

2、上下文管理:... 15

习题:... 17

 

 

 

冯诺依曼体系架构:

CPU:运算器和控制器组成;

运算器,完成各种算数运算、逻辑运算、数据传输等数据加工处理;

控制器,控制程序的执行;

存储器,用于记忆程序和数据,如内存;

输入设备,将数据或程序输入到计算机中,如keyboardmouse

输出设备,将数据或程序的处理结果展示给用户,如monitorprinter等;

一般说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,表示使用缺省大小的buffertb模式都是io.DEFAULT_BUFFER_SIZE

0,只在b二进制模式使用,表示关闭缓冲区;t不支持;

1,只在t文本模式使用,表示使用行缓冲,即见到换行符就flush()b就一个字节;

>1,用于指定buffer的大小,tio.DEFAULT_BUFFER_SIZEflush()完后会把当前字符串写入磁盘;b,表示行缓冲大小,缓冲区的值可以超过io.DEFAULT_BUFFER_SIZE,直到设定的值超出后才把缓冲区flush()

 

注:

二进制模式b,一个个字节操作,可指定buffer大小,使用io.DEFAULT_BUFFER_SIZE值,默认是40968192,单位byte

文本模式t,一般用默认缓冲区大小;如果是终端设备,是行缓存方式;如果不是终端设备,则使用二进制模式的策略;

tb两种模式下,使用buffering情况下,只要用过seek()tell(),就会flush()

flush(),将缓冲区数据写入磁盘;

close(),关闭前会调用flush()

In [2]: import io

In [3]: io.DEFAULT_BUFFER_SIZE   #缺省缓冲区大小,单位字节

Out[3]: 8192

cc++来说,缓冲区溢出是一种***手段(很多函数不作边界检查);在有边界检查的语言中,这种手段用不了;

一般来说,默认缓冲区大小是个比较好的选择,除非明确知道,否则不调整它;

一般编程中,明确知道需要写磁盘了,都会手动调用一次flush(),而不是等到自动flushclose的时候;

 

encoding,编码,仅t下使用:

None,表示使用缺省编码,依赖OS

 

其它参数:

errors

什么样的编码错误将被捕获;

NoneStrict一样,表示有编码错误抛ValueError异常;

ignore,忽略;

 

newline

文本模式中,换行的转换,可以为None''\r\n\r\n

读时,None表示\r\n\r\n都被转换为\n''表示不会自动转换通用换行符;其它合法字符表示换行符就是指定字符,就会按指定字符分行;

写时,None表示\n都被替换为系统缺省行分隔符,os.linesep\n''表示\n不替换;其它合法字符表示\n会被替换为指定的字符;

 

closefd

fdfile descriptor,关闭文件描述符;

True表示关闭它;

False会在文件关闭后保持这个描述符;

fileobj.fileno()查看,如f.fileno(),读(共用文件描述符);f.close()后,fd会释放;每次打开fd往后加,之前的fdOS有缓存,再次打开时可能会和之前的一样;

 

 

文件指针:

指向当前字节位置;

mode=r,指针起始在0

mode=a,指针起始在EOF

tell(),显示指针当前位置;

 

seek(offset[,whence])

移动文件指针位置,相对whence走;

offset,偏移多少字节,whence从哪里开始;

seek是跳字节,中文''utf-8要跳3bytecp936要跳2个字节;

在中文下用seek不好,一般在英文下用seek,一般跳至尾部;

一般头开始,或按换行一行行读,不是所有文件都可seek

 

文本模式下,支持从开头向后偏移的方式:

whence0,缺省值,表示从头开始,offset只能正整数;

whence1,表示从当前位置,offset只接受0

whence2,表示从EOF开始,offset只接受0

f.seek(0,1)   #没意义,原地不动;

f.seek(0,2)   #跳至末尾;

 

二进制模式下:

whence0,缺省值,表示从头开始,offset只能正整数;

whence1,表示从当前位置,offset可正负数;

whence2,表示从EOF开始,offset可正负数;

二进制模式支持任意起点的偏移,从头,从尾,从中间位置均可;

向后seek可以超界,但向前seek时不能超界,否则抛异常;

 

 

例:

In [36]: f=open('test','x')

In [37]: f   #linux下,编码为UTF-8win下,编码为cp936,类似GBK

Out[37]: <_io.TextIOWrapper name='test' mode='x' encoding='UTF-8'>

In [38]: f.close()   #有开有关,不可以只开不关

1.jpg

注:

简体中文:

GB2312,子集;

GBK,真超集,完全包括GB2312(是GB2312的超集),双字节编码(不是真正的编码),扩充了CJKjapanesekorea);

ANSI,即GBK

cp936code page代码页,等价于GBK,是映射到GBK上的,但绝不是UTF-8

GBK<->UTF-8,任意一个如果少某个字则对应不起来,会乱码;

需搞定:asciiGBKGB2312

要保证源与目标的编码一致;

 

半角,ascii字符;

ascii0-127个字符对所有编码都通用;

文件放在disk上,一般很少修改;

read()方法,读到最后读不出来,不会报错;



 2.jpg

3.jpg

注:

notepad++上,显示所有字符按钮,可看到换行符;

dos/windows,换行符为\r\n,上图CRLFANSI,即GBK

unix,换行符为\n,上图LFUTF-8

编辑->文档格式转换->转为UNIX格式;

编码->转为UTF-8BOM编码格式

 

例:

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)   #文本模式下,whence12offset不支持非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可用12

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要跳3bytecp936要跳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()

 

例:

linuxtest文件(内容'')拷至win下,用ipython打开;

4.jpg

5.jpg


win下新建test文件,用notepad++打开;

6.jpg

7.jpg

8.jpg


 

 

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