IO编程
IO 即Input/Output input stream 就是数据从外面(磁盘、网络)流进内存,output stream 就是数据从内存流到外面去。
通常cpu 和 内存的速度远远高于外设的速度,所以在IO编程中,存在速度严重不匹配的问题。
例,把100M的数据写入磁盘,CPU输出100M的数据只需要0.01秒,可是磁盘要接收这100M数据可能需要10秒。于是有同步IO和异步IO;
同步IO是指 CPU等程序执行完后续代码,等100M的数据在10秒后写入磁盘,再接着往下执行
异步IO是指 CPU不等待,在磁盘写数据的时候,CPU去执行其他的程序。
异步IO编写的程序性能远高于同步IO,但是异步IO的编程模型复杂。
操作IO的能力都是由操作系统提供的,每一种编程语言都会把操作系统提供的低级C接口封装起来方便使用,Python也一样。
open()
* UnicodeDecodeError 非法编码的字符可通过 errors参数设置为忽略
字符串的读取可以添加参数encoding,binary文件的读取不可设置此参数
f=open('/users/michael/gbk.txt','r',encoding='utf8',errors='ignore')
打开文件的模式有 'r',‘w','a' 读,写,添加
’r+','w+','a+' +表示以前面的那个字符的模式打开文件,除了那个字符的模式外还可以进行另一种模式的操作
'rb','wb,'ab' b表示该文件是二进制文件,比如图片、视频等
r 只读模式,文件不存在会报错,指针位于文件开头
w 只写模式,文件不存在则自动创建,文件存在会先清空内容再写,指针位于文件末尾
a 添加模式,文件不存在则创建,文件存在则在末尾接着写,指针位于文件末尾
r+ 打开后可读写,文件不存在会报错,指针位于文件开头,写文件会逐字覆盖,写完后指针位于写的最后一个字节处,再read()会从当前位置接着往后读
w+ 打开后可读写,文件不存在则创建,文件存在则会清空已存在的内容,指针位于文件末尾
a+ 打开后可读写,文件不存在则创建,存在则从文件末尾开始读写,指针位于文件末尾
当指针位于文件末尾时,可以使用seek()来移动文件中指针的位置
f=open(filename,'w+')
f.seek(0) #将指针移到文件开头
f.read()
f.seek(-10,2) #seek的第二个参数为2时表示从文件的末尾处开始移动,-10表示向前移动10个字节
读文件
不传入open()的第二个参数,则默认为‘r'读模式
f=open('e:/work/e.txt','r')
#'r'表示读
print(f.read())
#如果文件打开成功,调用read()方法可以一次性读取全部内容,Python把内容读到内存,用一个str 对象表示
#最后一步是调用close()方法关闭文件。文件使用完毕后必须关闭,因为文件对象会占用系统资源,并且操作系统同时能打开的文件数量也是有限的:
#f.close()
#由于文件读写时都可能产生IOError,一旦出错,后面的f.close()就不会调用。所以,为了保证无论是否出错都能正确地关闭文件,我们可以使用try...finally来实现:
'''
try:
f=open('/path/to/file','r')
print(f.read())
finally:
if f:
f.close()
'''
#但是每次都这么写实在太繁琐,所以,Python 引入了with语句来自动帮我们调用close()方法;
'''
with open('/path/to/file','r') as f:
print(f.read())
'''
#这和前面的try...finally 是一样的,但是代码更简洁,并且不必调用f.close()方法。
#调用read()会一次性读取文件的全部内容,如果文件有10G,内存就爆了,所以,要保险起见,可以反复调用read(size)方法,每次最多读取size个字节的内容。
#调用readline()可以每次读取一行内容,调用readlines()一次读取所有内容并按行返回list,每个字符串值都以一个换行符\n结束。可以根据需要选择合适的调用。
for line in f.readlines():
print(line.strip()) # 把末尾的'\n'删掉
f.close()
* file-like object
像open()函数返回的这种有个read()方法的对象,在Python中统称为file-like object 。除了file外,还可以是内存的字节流,网络流,自定义流等等。
#file-like object 不要求从特定类继承,只要写个read()方法就行。
StringIO就是在内存中创建的file-like object ,常用作临时缓冲。
StringIO在内存中读写str
BytesIO在内存中读写二进制数据bytes
#要把str写入StringIO,我们需要先创建一个StringIO,然后,像文件一样写入即可:
'''
>>> from io import StringIO
>>> f=StringIO()
>>> f.write('hello')
5
>>>f.write(' ')
1
>>> f.write('jsdjk')
>>> print(f.getvalue())
hello jsdjk
'''
#交互式环境下f.write()返回的是写入的str的长度
#getvalue()方法用于获得写入后的str.
#要读取StringIO,可以用一个str初始化StringIO,然后像读文件一样读取:
from io import StringIO
f=StringIO('Hello!\nHi!\nGoodbyte!')
while True:
s=f.readline()
if s=='':
break
print(s.strip())
#strip()的作用是去掉字符串前后的空格
#b'\xe4\xb8\xad\xe6\x96\x87'
#BytesIO的使用类似StringIO,需创建一个BytesIO,然后写入一些bytes:
from io import BytesIO
f=BytesIO()
f.write('中文'.encode('utf-8'))
print(f.getvalue())
#请注意,写入的不是str,而是经过utf-8编码的bytes.
#用一个bytes初始化bytesIO,然后,像读文件一样读取:
f=BytesIO(b'\xe4\xb8\xad\xe6\x96\x87')
print(f.read())
如果传递给open()的文件名不存在,写模式和添加模式都会创建一个新的空文件,在读取或写入文件后,调用close()方法,然后才能再次打开该文件。
写文件时,操作系统往往不会立刻把数据写入磁盘,而是放到内存缓存起来,空闲的时候再慢慢写入。
只有调用close()方法时,操作系统才保证把没有写入的数据全部写入磁盘。忘记调用close()的后果是数据可能只写了一部分到磁盘,剩下的丢失了。所以还是用with语句来的保险:
with open('e:/work/w.txt','w') as f:
f.write('this is write with python\'s \'with\' function,whitch dosen\'t need \'close\' function to close fiel')
* shelve模块保存变量
#利用shelve模块,可以将Python程序中的变量保存到二进制的shelf文件中,程序就可以从硬盘中恢复变量的数据。
import shelve
shelfFile=shelve.open('mydata') 有则打开,没有则创建mydata
cats=['Zophis','Pooks','Simon']
shelfFile['cats']=cats
shelfFile.close()
#我们创建了一个列表cats,并写下shelfFile['cats']=cats,将该列表保存在shelfFile中,作为键’cat'关联的值(就像在字典中一样)。
#在Windows上运行以上代码,可以在当前工作目录下看到3个新文件;mydata.bak/mydata.dat和mydata.dir .
#重新打开这些文件取出数据时,shelf值不必用读模式或写模式打开,因为它们在打开后,既能读又能写。
shefFile=shelve.open('mydata')
type(shelfFile)
#<class 'shelve.DbfilenameShelf'>
print(shelfFile['cats'])
#['Zophis', 'Pooka', 'Simon']
shelfFile.close()
#这里,我们打开了shelf文件,检查我们的数据是否正确存储。输入shelfFile['cat']将返回我们前面保存的同一个列表,所以我们就知道该列表得到了正确存储,
#然后我们调用close().
#就像字典一样,shelf值有keys()和values()方法,返回shelf中键和值的类似列表的值。
#因为这些方法返回类似列表的值,而不是真正的列表,所以应该将它们传递给list()函数,取得列表的形式。
shelfFile=shelve.open('mydata')
print(list(shelfFile.keys()))
print(list(shelfFile.values()))
* pprint.pformat()函数保存变量
import pprint
cats=[{'name':'Zophie','desc':'chubby'},{'name':'Pooka','desc':'fluffy'}]
pprint.pformat(cats) #返回cats变量的字符串形式
fileObj=open('myCats.py','w')
fileObj.write('cats= '+pprint.pformat(cats)+'\n')
fileObj.close()
#当我们有了cats中数据的字符串形式,就很容易将该字符串写入一个文件,我们将它命名为myCats.py
#import 语句导入的模块本身就是Python脚本。如果来自pprint.pformat()的字符串保存为一个.py文件,该文件就是一个可以导入的模块,像其他模块一样。
#由于Python脚本本身也是带有.py文件扩展名的文本文件,所以你的Python程序甚至可以生成其他Python程序。然后将这些文件导入到脚本中。
import myCats
print(myCats.cats)
#[{'name':'Zophie','desc':'chubby'},{'name':'Pooka','desc':'fluffy'}]
print(myCats.cats[0])
#{'name':'Zophie','desc':'chubby'}
myCats.cats[0]['name']
#'Zophie'
#创建一个.py文件(而不是利用shelve模块保存变量)的好处在于,因为它是一个文本文件,所以任何人都可以用一个简单的文本编辑器读取和修改该文件的内容。
#但是,对于大多数应用,利用shelve模块来保存数据,是将变量保存到文件的最佳方式。
#只有基本数据类型,诸如整型、浮点型、字符串、列表和字典,可以作为简单文本写入一个文件。例如,File对象就不能编码为文本。
* 序列化
#在程序运行的过程中,所有的变量都在内存中,例,定义一个dic:
d=dict(name='Bob',age=20,score=88)
#可以随时修改变量,例把name改成bill,但是一旦程序结束,变量所占用的内存就被操作系统全部回收。如果没有把修改后的bill存储到磁盘上,下次重新运行程序
#变量又被初始化为Bob。
变量从内存中变成可存储或传输的过程称之为序列化,在Python中叫pickling,在其他语言中也被称之为serialization,marshalling,flattening等
#序列化后,就可以把序列化后的内容写入磁盘,或者通过网络传输到别的机器上。
把变量内容从序列化的对象重新读到内存里称之为反序列化,即unpickling
import pickle
d=dict(name='Bob',age=20,score=88)
pickle.dumps(d)
#pickle.dumps()方法把任意对象序列化成一个bytes,然后,就可以把这个bytes写入文件。
#或者用另一个方法pickle.dump()直接把对象序列化后写入一个file-like object:
f=open('dump.txt','wb')
pickle.dump(d,f)
f.close()
#当我们要把对象从磁盘读到内存时,可以先把内容读到一个bytes,然后用pickle.loads()方法反序列化出对象,
#也可以直接用pickle.load()方法从一个file-like object中直接反序列化出对象。
f=open('dump.txt','rb')
d=pickle.load(f)
f.close()
print(d)
#{'age': 20, 'name': 'Bob', 'score': 88}
#pickle的问题和所有其他编程语言特有的序列化问题一样,就是它只能用于Python,并且可能不同版本的Python彼此都不兼容,因此,只能用pickle保存那些不重要的
操作文件和目录
import os
#在操作系统中定义的环境变量,全部保存在os.environ这个变量中,可以直接查看:
print(os.environ)
'''
environ({'PROCESSOR_ARCHITECTURE': 'AMD64', 'PROCESSOR_IDENTIFIER': 'Intel64 Family 6 Model 60 Stepping 3, GenuineIntel', 'PSMODULEPATH': 'C:\\Program Files\\WindowsPowerShell\\Modules;C:\\Windows\\system32\\WindowsPowerShell\\v1.0\\Modules', 'USERDNSDOMAIN': 'GTICOM.CN',
...})
'''
#要获取某个环境变量的值,可以调用os.environ.get('key'):
print(os.environ.get('PATH'))
'''
'C:\\ProgramData\\Oracle\\Java\\javapath;C:\\Windows\\system32;C:\\Windows;C:\\Windows\\System32\\Wbem;C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\;D:\\Program Files\\TortoiseSVN\\bin;E:\\work;C:\\Users\\li.wu\\AppData\\Local\\Programs\\Python\\Python35\\Scripts\\;C:\\Users\\li.wu\\AppData\\Local\\Programs\\Python\\Python35\\;C:\\Users\\li.wu\\AppData\\Local\\Microsoft\\WindowsApps;C:\\Program Files\\Java\\jdk1.8.0_73\\bin;'
'''
os.environ.get('x','default')
#'default'
#操作文件和目录的函数一部分放在os模块中,一部分放在os.path模块中。
os.getcwd() 获取当前工作目录
os.chdir('e:/work') 变更当前工作目录为e:/work
os.path.abspath('.') 查看当前目录的绝对路径
os.path.join('e:/work','exam') 合并路径
os.mkdir('e:/work/exam') 创建目录
os.makedirs('e:\delicious\walnut\waffles') 有必要时创建所有目录
os.makedirs()将创建所有必要的中间文件夹,目的是确保完整路径名存在
os.rmdir('e:/work/exam') 删除目录
os.rename('w.txt','test.py') 对文件重命名
os.remove('test.py') 删除文件
os.listdir(dirname) 返回dirname下的所有目录和文件
列出当前目录下所有的.py文件
[x for x in os.listdir('.') if os.path.isfile(x) and os.path.splitext(x)[1]=='.py']
#['err.py', 'hell.py', 'hello.py', 'IOExample.py', 'learning.py', 'mydict2.py', 'mydict_test.py']
os.curdir 返回当前目录'.'
os.path.isdir(name) 判断name是不是一个目录,name不是目录就返回false
os.path.isfile(name) 判断name是不是一个文件,不存在name也返回false
os.path.isabs(path) 判断是否是绝对路径,就返回True,如果参数是一个相对路径,就返回False.
os.path.exists(name) 判断是否存在文件或目录name
os.path.getsize(name) 获得文件大小,如果name是目录返回0
os.path.abspath(name) 获得绝对路径
os.path.normpath(path) 规范path字符串形式
os.path.basename(path) 返回文件名
os.path.basename(path)将返回一个字符串,它包含path参数中最后一个斜杠之后的所有内容。
os.path.dirname(path) 返回文件路径
os.path.dirname(path)将返回一个字符串,它包含path参数中最后一个斜杠之前的所有内容
os.path.relpath(path,start) 返回相对路径
从start路径到path的相对路径的字符串。如果没有提供start,就使用当前工作目录作为开始路径。
通过os.path.join()函数,可以正确处理不同操作系统的路径分隔符。
#在Linux/Unix/Mac下,os.path.join()返回的路径分隔符是'/',在windows下os.path.join()返回的路径分隔符是'\'.
os.path.split()函数 分割目录中的路径与文件
os.path.split('/Users/michael/testdir/file.txt')
#('/Users/michael/testdir', 'file.txt')
os.path.sep 返回路径中的分隔符
calcFilePath='c:\\Windows\\System32\\calc.exe'
calcFilePath.split(os.path.sep)
#['c:', 'Windows', 'System32', 'calc.exe']
os.path.splitext() 分割目录与文件的扩展名
os.path.splitext('/path/to/file.txt')
#('/path/to/file', '.txt')
这些合并、拆分路径的函数并不要求目录和文件要真实存在,它们只对字符串进行操作
#复制文件的函数copyfile()可在shutil模块中找到,shutil模块中还有很多其他的实用函数。
* os.walk
for i,j,k in os.walk('c:\\delicious'): 在循环的每次迭代中,返回3个值:当前文件夹名称的字符串(c:\delicious)、当前文件夹中子文件夹的字符串的列表、当前文件夹中文件的字符串列表。
单个的句点(‘点’)用作文件夹目录名称时,是‘这个目录’的缩写。 两个句点(‘点点’)意思是父文件夹。
* shutil模块
shutil.copy(source,destination) 复制文件
将路径source处的文件复制到路径destination处的文件夹。如果destination是一个文件名,它将作为被复制文件的新名字。
#该函数返回一个字符串,表示被复制文件的路径。
import shutil,os
os.chdir('e:\work')
shutil.copy('e:\work\dump.txt','e:\delicious\dump3.txt')
#'e:\\delicious\\dump3.txt'
shutil.copy('e:\work\dump.txt','e:\delicious')
#decode('gbk').encode('utf-8')
#表示先将其gbk解码,再用utf-8编码,再输出
#decode('utf-8').encode('gbk')
#表示先对其utf-8解码,然后再gbk编码。