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()

 

posted @ 2018-04-03 22:38  tsembrace  阅读(586)  评论(0编辑  收藏  举报