Python学习笔记十 IO编程
参考教程:廖雪峰官网https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000
IO编程
IO在计算机中指Input/Output,也就是输入和输出。比如你打开浏览器,访问网页,浏览器就需要通过网络IO获取网页信息。浏览器首先会发送数据给服务器,告诉它我想要首页的HTML,这个动作是往外发数据,叫Output,随后新浪服务器把网页发过来,这个动作是从外面接收数据,叫Input。所以,通常,程序完成IO操作会有Input和Output两个数据流。当然也有只用一个的情况,比如,从磁盘读取文件到内存,就只有Input操作,反过来,把数据写到磁盘文件里,就只是一个Output操作。IO编程中,Stream(流)是一个很重要的概念,Input Stream就是数据从外面(磁盘、网络)流进内存,Output Stream就是数据从内存流到外面去。对于浏览网页来说,浏览器和服务器之间至少需要建立两个通道,才可以既能发数据,又能收数据。
一、文件读写
在磁盘上读写文件的功能都是由操作系统提供的,现代操作系统不允许普通的程序直接操作磁盘,所以,读写文件就是请求操作系统打开一个文件对象(通常称为文件描述符),然后,通过操作系统提供的接口从这个文件对象中读取数据(读文件),或者把数据写入这个文件对象(写文件)。
(一)读文件
Python内置的open()函数可以打开一个文件对象:
f = open('/Users/michael/test.txt', 'r')
其中第二个参数'r'表示以读(read)的方式打开。如果文件不存在,这个函数就会抛出一个IOError的错误。
如果文件打开成功,则可以继续调用read()方法把文件内容一次全部读取到内存:
f.read()
返回的是文件内容组成的一个字符串。
在使用完文件后必须用close()关闭文件。否则文件对象会占用操作系统资源。
文件读写过程都会可能产生IOError,所以可以使用try...finally..语句:
try: f=open('...','r') finally: if f: f.close()
如果觉得这些代码繁琐,也可以使用with语句,并且with语句后面不用调用close()方法:
with open('test','r') as f: print(f.read())
调用read()
会一次性读取文件的全部内容,如果文件有10G,内存就爆了,所以,要保险起见,可以反复调用read(size)
方法,每次最多读取size个字节的内容。另外,调用readline()
可以每次读取一行内容,调用readlines()
一次读取所有内容并按行返回list
。因此,要根据需要决定怎么调用。如果文件很小,read()
一次性读取最方便;如果不能确定文件大小,反复调用read(size)
比较保险;如果是配置文件,调用readlines()
最方便:
for line in f.readlines(): print(line.strip())
二进制文件
前面提到的文件都是文本文件,并且必须是UTF-8编码的文本文件,如果要读取二进制文件,比如图片、视频等,则需要用'rb'模式打开文件:
with open("1.jpg",'rb') as f: print(f.read(8))
字符编码
如果要读取非UTF-8的文本文件,需要给open()传入encoding参数:
#指定为GBK编码 open('....txt', 'r', encoding='gbk')
如果遇到有些编码不规范的文件,可能会出现UnicodeDecodeError错误,遇到这种情况,可以给open()函数增加设置一个errors参数,表示遇到编码错误后如何处理,如下:忽略是其中一种处理的方式:
open('/Users/michael/gbk.txt', 'r', encoding='gbk',errors='ignore')
写文件
写文件和读文件的唯一区别是在调用open()函数时,传入标识符'w'或'wb'表示写文本文件或者写二进制文件:
open('/Users/michael/gbk.txt', 'w') f.write("Hello World!")
当我们写文件时,操作系统往往不会立刻把数据写入磁盘,而是放到内存缓存起来,空闲的时候再慢慢写入。只有调用close()
方法时,操作系统才保证把没有写入的数据全部写入磁盘。忘记调用close()
的后果是数据可能只写了一部分到磁盘,剩下的丢失了。所以,还是用with
语句来得保险:
with open('....txt','w') as f: f.write("HELLO WORLD!")
同样如果需要写入特定编码的文本文件,需要给open()函数传入encoding参数,将字符串自动转换成指定编码。
另外,当以'w'模式写入文件时,如果文件已经存在,会直接覆盖原有内容。那么需要传入'a'以追加模式写入文件。
with open("1.txt",'a') as f: f.write('i love nieson!\n') with open("1.txt",'r') as f: print(f.read())
二、StringIO和BytesIO
(一)StringIO
有时候不一定是针对文件的读写,也可以在内存中创建虚拟文件进行读写,StringIO就提供了这种方式。
要把str写入StringIO,我们需要先创建一个StringIO,然后即可像文件一样操作:
from io import StringIO f=StringIO() #创建一个StringIO文件 f.write('hello') f.write(' world!') #注意这里第二次写入不会覆盖,直接append print(f.getvalue()) #注意这里用getvalue()获得写入的str
上述的最后一句代码也可以用以下代码替换:
f.seek(0) print(f.read())
要读取StringIO,可以先用一个str初始化,然后再类似于读取文件一样:
f=StringIO("Hello!\nHi!\nGoodBye!") while True: s=f.readline() if s=='': break print(s.strip())
(二)BytesIO
StringIO操作的是str,如果要直接操作二进制数据,需要使用BytesIO,操作类似于StringIO:
from io import BytesIO f=BytesIO() #创建一个ByteIO文件 f.write('中文'.encode('utf-8')) #写入“中文”的UTF-8编码 print(f.getvalue()) #输出编码 f=BytesIO(b'\x11\xaa\xf0\x98\x99') #直接创建一个包含二进制数据的ByteIO文件 print(f.read()) #读取二进制数据
三、操作文件和目录
Python内置的os模块可以直接调用操作系统提供的接口函数:
import os print(os.name) #打印操作系统信息
import os print(os.path.abspath('-@-')) #打印当前目录的绝对路径,以参数符号结尾 #在某个目录下创建一个新目录,首先要用os.path.join()把新目录完整路径表示出来 print(os.path.join('D:\STUDY\workspace\Python\LXF','testdir')) #第二步再创建这个目录 os.mkdir('D:\\STUDY\\workspace\\Python\LXF\\testdir') #删除一个目录的操作: os.rmdir('D:\\STUDY\\workspace\\Python\LXF\\testdir')
os.path.splitext()
可以直接得到文件扩展名:
os.path.splitext('/path/to/file.txt') #返回:('/path/to/file', '.txt')
重命名和删除文件的操作:
# 对文件重命名: os.rename('test.txt', 'test.py') # 删掉文件: os.remove('test.py')
os.listdir():返回指定路径下的文件和文件夹的列表,参数为指定路径,默认为当前目录:
import os import os.path for f in os.listdir(): print(f)
四、序列化
程序运行过程,所有变量都是存储在内存中,一旦程序结束,变量所占用的内存就会被回收。Python中可以把变量从内存中变成可存储或传输的信息,这个过程称为序列化即picking。
序列化之后,就可以把序列化的内容写入磁盘,或者通过网络传输到其他设备。反过来,把变量内容从序列化的对象重新读取到内存的过程称之为反序列化,即unpicking。
Python提供了pickle模块来实现序列化。
pickle.dumps()函数可以将任意对象(参数)序列化成一个bytes,然后就可以把这个bytes写入文件。
pickle.dump()函数有两个参数,第一个为需要序列化的对象,第二个为写入的目的文件,可以直接把序列化后的内容写入一个文件。
import pickle d=dict(name='Bob',age=20,score=88) pickle.dumps(d) #把变量d序列化 print(pickle.dumps(d)) #打印序列化的信息 #以二进制写入方式打开文件 f=open('dump.txt','wb') #将序列化信息写入文件对象 pickle.dump(d,f) f.close() #以二进制读取方式打开文件 f=open('dump.txt','rb') print(f.read()) f.close()
反之,pickle.loads()方法反序列化对象,先把内容读到一个bytes中作为参数传入方法;也可以直接用pickle.load()方法从一个文件对象中直接反序列化出对象。
import pickle f=open('dump.txt','rb') #使用pickle.loads()其参数必须为一个序列化的内容 print(pickle.loads(f.read())) f.seek(0) #使用pick.load()其参数可以直接为一个存放序列化内容的文件对象 d=pickle.load(f) print(d) f.close()
序列化的方法(形式)有很多种,一般采用JSON标准可以被所有语言读取,也可以方便的存储和传输,也可以直接在Web页面读取。
JSON表示的对象就是标准的JavaScript语言的对象。
Python 内置的json模块提供了Python对象到JSON的格式转换。
以下示例为将一个Python对象编程一个JSON格式对象:
import json d=dict(name='Bob',age=20,score=88) #输出:'{"name": "Bob", "age": 20, "score": 88}' print(json.dumps(d))
这里json.dumps()方法返回一个字符串,字符串的内容就是标准格式的JSON。
类似的,json.dump()方法可以把这个字符串直接写入一个文件中。
f=open('t1.txt','w') json.dump(d,f) f.close()
当要把JSON反序列化为Python对象时,就需要json.loads()或json.load()方法:
import json f=open('t1.txt','r') print(json.loads(f.read())) f.seek(0) print(json.load(f)) f.close()