01-上章题目讲解:在上一章节里
02-open方法和模式
文件操作:非常重要
冯诺依曼体系架构
内存:掉电易失
CPU由运算器和控制器组成:
- 运算器,完成各种算数运算,逻辑运算,数据传输等数据加工处理
- 控制器,控制程序执行
- 存储器,用于记忆程序和数据,例如内存
- 输入设备,将数据或者程序输入到计算机中,例如键盘,鼠标
- 输出设备,将数据或程序的处理结果展示给用户,例如显示器,打印机等
一般说IO操作,指的是文件IO,如果指的是网络IO,都会直接说网络IO
机械磁盘上存的都是二进制,且存储的数据未必是连续的,所以叫机械硬盘也叫随机访问设备。
文件IO常用操作
打开操作
文件系统是靠目录管理,文件一定是放在目录下的;windows和linux文件系统不一样;windows系统是带逻辑驱动器,逻辑驱动器有自己的根,因为有逻辑驱动器,所以要加盘符;linux是根文件系统。
f = open(file, mode='r', buffering=None, encoding=None, errors=None, newline=None, closefd=True,opener=None)
打开一个文件,返回一个文件操作对象(流对象)和文件描述符。打开文件失败,则返回异常。打开后一定要关闭。
基本使用:
创建一个文件test,然后打开它,用完关闭
其中的encoding='cp936',cp936的cp代表codepage,等于gbk编码;windows默认编码是gbk,linux这是UTF-8;gbk编码中文是2字节;UTF-8 编码常见中文是3个字节;GBK兼容GB2312编码,但比GB2312包含了更多的汉字:中文存储时,第一个字节码值在127256之间,第二个字节码值在0256之间。
Ipython环境下运行shell命令(前提是linux系统装的有ipython,这样在ipython环境下运行shell命令时才可以),需要在命令前加 ! ,例如 !ls
文件访问的模式有2种:文本模式(字符访问,与编码有关)和二进制模式(字节流访问,不管编码)。不同模式下,操作函数不尽相同,表现的结果也不一样。
open的参数
mode模式
r read
只读打开文件,如果使用write方法会抛异常。
如果文件不存在,抛出FileNotFoundError异常
w write
表示只写方式打开,如果读取则抛出异常
如果文件不存在,则直接创建文件
如果文件存在,则清空文件内容
w:没有的话创建,有的话直接覆盖
f = open('test','w')
f.close
以上两行可以创建一个空文件
print后 会返回写进去的字符数量
x create
文件不存在直接创建,并只写方式打开
文件已存在,会抛FileExistsError异常
a append
文件存在,只写打开,追加内容
文件不存在,则创建后,只写打开,追加内容。
总结以上几种模式
r是只读,wxa都是只写。
wxa都可以产生新的文件,w不管文件存在与否,都会生成全新内容的文件;a不管文件是否存在,都能在打开的文件尾部追加;x必须要求文件事先不存在,自己造一个新文件。
文本模式t:操作单位是字符
字符流,将文件的字节按照某种字符编码理解,按照字符操作。open的默认mode是rt
二进制模式b:
字节流,将文件按照字节理解,与字符编码无关。二进制模式操作时,字节操作使用bytes类型
一个文件读取的编码方式要和写入时的编码方式一致,否则读取时会出现乱码。
二进制操作写入中文到文件中:
f = open('test3','wb')
#f.write('啊') #TypeError: a bytes-like object is required, not 'str' 需要像bytes一样的东西,不要字符串
#f.write(b'啊') #bytes can only contain ASCII literal characters 只认ASII,这样写不行
f.write('啊'.encode('gbk')) #encode 默认编码方式为 utf-8. 啊 用utf-8编码占3个字节:b'\xe5\x95\x8a' 汉字的正确编码方式为gbk
f.close()
f = open('test3','r',encoding='gbk')
print(f.read())
f.close()
一般来讲,各种编码都兼容ASII码
各种编码都是二进制字节流去根据各自编码表对应不同的字符;二进制字节流本身都是ASII码。
+
+ 为r,w,a,x提供缺失的读写功能,但是,获取文件对象依旧按照r,w,a,x自己的特征。
+ 不能单独使用,可以认为它是为前面的模式字符做增强功能。
03-文件指针
文件指针:指向当前字节位置
mode=r,指针起始位置在0
mpde=a,指针起始位置在EOF
tell()显示指针当前位置
seek(offset[,whence]):移动指针位置。offset偏移多少字节,whence从哪里开始。
tell()和seek()都是针对字节的偏移,所以用他们处理字符流有风险
文本模式下:
- whence 0 缺省值,表示从头开始,offset只能接受0和正整数
- whence 1 表示从当前位置,offset只接受0,相当于原地不动,所以没什么用
- whence 2 表示EOF开始,offset只接受0,相当于移动文件指针到EOF。
- seek是按照字节偏移的。
- 文本模式下只支持从开头向后偏移的方式
二进制模式下
- whence 0缺省值,表示从头开始,offset只能是正整数,包括0
- whence 1表示从当前位置,offset可正可负
- whence 2表示从EOF开始,offset可正可负
- 二进制模式支持任意起点的偏移,从头,从尾,从中间位置开始。
- 向后seek可以超界,但是向前seek的时候,不能超界,否则会抛异常。
linux 下删除一个文件的原理是将该文件的inode扔出去,让别的文件使用,从而覆盖掉改文件。
inode:索引节点,它用来存放档案及目录的基本信息,包含时间、档名、使用者及群组等。
04-缓冲区原理
缓存和缓冲的区别:
缓存:数据结构为dict,目的是快速找到想要的内容
缓冲:数据结构为list,一堆数据排队放在那
04-缓冲区原理
buffering:缓冲区
- open的一个参数
- -1 表示使用缺省大小的buffer。
- 如果是二进制模式,使用io.DEFAULT_BUFFER_SIZE值,默认是4096或者8192,目前多是8192 个字节。
- 如果是文本模式:
- 如果是终端设备,是 行缓存方式
- 如果不是,则使用二进制模式的策略。
- 0 只在二进制模式使用,表示关闭buffer,即不需要内存buffer,可以看做是一个FIFO的文件。
- 1 只在文本模式使用,表示使用行缓冲。意思是见到换行符就flush
- 大于1 用于指定buffer的大小;但是在文本模式下,通过实验发现还是在用buffer的默认值。
buffer缓冲区
缓冲区是内存中的一个连续的空间,一般来说是一个FIFO(先进先出)队列(不支持中间插入,只支持两头操作),到缓冲区满了或者达到阈值,数据才会flush到磁盘。
内存的运行速度比I/O设备高的多
flush()将缓冲区的数据写入磁盘
close()关闭前会调用flush()
内存的东西不是能直接写到磁盘上去的,从内存空间的用户空间,搬到操作系的内核空间,从内核空间写到磁盘缓冲区里,由磁盘缓冲区写入磁盘
buffering 总结
一般说来只需记得:
- 文本模式,一般都用默认缓冲区大小
- 二进制模式,是一个个字节的操作,可以指定buffer的大小
- 一般来说,默认缓冲区大小是个比较好的选择,除非明确知道,否则不调整它。
- 一般编程中,明确知道需要写磁盘了,都会手动调用一次flush(f.flash()),而不是等到自动flush或者close的时候。
05-编码描述符及读写方法
encoding:编码,仅文本模式使用
None表示使用缺省编码,依赖操作系统,windows下缺省GBK,linux下缺省UTF-8
UTF-8:用多个字节编码世界上的所有文字;大多数中文落在3个字节上
UTF-8(8-bit Unicode Transformation Format)是一种针对Unicode的可变长度字符编码,也是一种前缀码,又称万国码。
GBK:单字节+双字节。ASCII单字节,中文双字节;CODEPAGE=936 ,代表GBK
跨平台的时候建议用UTF-8
GBK和UTF-8之间编码不一致,需要查表
其他参数
errors
什么样的编码错误将被捕获
None和strict表示编码错误将抛出ValueError异常;ignore表示忽略。
一般都用默认,不用管。
newline
文本模式中换行的转换。可以为None,"空串、'\r'、'\n'、'\r\n'
读时,None表示'\r'、'\n'、'\r\n'都被转换为'\n'
写时,None表示'\n'都会被替换为系统却省行分隔符os.linesep;
f = open("D:/magedu/test",'w')
f.write('py\rwww\nmaedu\r\npython3')
f.close()
newlines = [None,'','\n','\r\n']
for nl in newlines:
f = open("D:/magedu/test",'r+',newline=nl)
#print(f.readlines())
print(f.read().encode()) #通过编码可以将换行符保留,而不是直接去做换行
f.close()
closefd
关闭文件描述符,True表示关闭它。False会在文件关闭后保持这个描述符。
fileobject.fileno()查看。
f = open("D:/magedu/test",'w')
print(f.fileno()) #文件描述符
文件打开时就会产生一个文件对象。
read
read(size=-1)
size表示读取的多少个字符或字节;负数或者None表示读取到EOF
f = open("D:/magedu/test",'w')
f.write('python\r\n123\r\npython3')
f.close()
f = open("D:/magedu/test",'r+')
print(f.read().encode()) #指针到最后
f.seek(0) #文件指针回到开头
print(f.readline(1).encode()) #一个个字符读
f.close()
#以后读取文件的每一行可以按照如下的读,可以少写一行
f = open("D:/magedu/test",'r')
for line in f:
print(line)
write
write(s),把字符串s写入到文件中并返回字符的个数;常用
writelines(lines),将字符串列表写入文件
要自己提供换行符
close
flush并关闭文件对象
文件已经关闭,再次关闭没有任何效果
其他
seekable() :是否可seek
readable():是否可读
writable():是否可写
closed 是否已经关闭
f = open("D:/magedu/test",'r')
print(f.seekable()) # True
print(f.readable()) # True
print(f.writable()) # False
print(f.closed) # False
for line in f:
print(line)
f.close()
print(f.closed) # True
06-上下文管理
问题的引出
在linux中执行如下代码:
lst = []
for _ in range(2000):
lst.append(open('test'))
#OSError:[Errorno 24] Too many open files:'test'
print(len(list))
lsof 列出打开的文件。没有就 # yum install lsof
$lsof -p 1427|grep test|wc -l
lsof -p 进程号
ulimit -a 查看所有限制。其中open files 就是打开文件数的限制,默认1024