Python学习笔记–8.I/O
2015-07-21
MrZigZag @ Peking University
1.文件
Python内置读写文件的函数,用法与C类似。读写文件一般都是请求操作系统打开一个文件对象,然后通过操作系统的借口从这个文件对象读写数据。
读文件,使用内置的open()
函数,传入文件名和读写标识符。
f = open('file_name', 'r')
调用read()
方法可以一次性读取全部内容存放到一个str中,最后调用close()
关闭文件。文件使用完毕之后必须关闭。每次都写close()
并不方便,Python提供了with
语句自动调用close()
。
with open('file_name', 'r') as f:
str = f.read()
与Java类似,Python要求读文件时要调用try-except
机制来处理可能会出现的问题。
try:
f = open('file_name', 'r')
str = f.read()
finally:
if f:
f.close()
读文件还可以使用read(size)
每次读取size字节的内容,或者用readline()
读取一行,还可以用readlines()
一次读取所有行并生成一个list。
读二进制文件,用rb
模式打开文件即可。
with open(file_name, 'rb') as f:
u_str = f.read()
一般而言,读取非Ascii编码的文本文件都是先用二进制打开,读取再解码。Python提供codecs
模块处理读文件时的自动编码。
import codecs
with codecs.open(file_name, 'r', 'gbk') as f:
f.read()
写文件,与读文件类似,区别在于用了write()
,使用标识符w
或者wb
。同时可使用with
。要写入特定编码的文件,可以使用codecs
。
2.操作文件和目录
Python内置了os
模块,可以直接调用操作系统提供的接口函数。例如:
>>> import os
>>> os.name
'posix'
>>> os.uname() #操作系统的详细信息
('Darwin', 'MrZigZagdeMacBook-Pro.local', '14.4.0', 'Darwin Kernel Version 14.4.0: Thu May 28 11:35:04 PDT 2015; root:xnu-2782.30.5~1/RELEASE_X86_64', 'x86_64')
>>> os.environ #环境变量
{'VERSIONER_PYTHON_PREFER_32_BIT': 'no', 'TERM_PROGRAM_VERSION': '326', 'LOGNAME': 'michael', 'USER': 'michael', 'PATH': '/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/opt/X11/bin:/usr/local/mysql/bin', ...}
>>> os.getenv('PATH') #获取环境变量的值
'/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/texbin:/Library/Java/JavaVirtualMachines/jdk1.8.0_45.jdk/Contents/Home/bin'
操作文件和目录的函数放在os
和os.path
模块中。
>>> os.path.abspath('.') # 当前目录的绝对路径
'/Users/mrzigzag'
将两个路径组合时,根据操作系统的差异性,一般不要直接拼接字符串,最好用os.path.join()
函数,这样可以处理不同操作系统的路径分隔符。
拆分路径时用os.path.split()
可以将路径拆分成 两个部分,最后一个部分总是最后级别的目录或者文件名。得到文件扩展名os.path.splitext()
。
创建目录os.mkdir(dir)
,删除目录os.rmdir(dir)
。
>>> os.path.join('/Users/mrzigzag', 'testdir')
'/Users/mrzigzag/testdir'
>>> os.mkdir('/Users/mrzigzag/testdir')
>>> os.rmdir('/Users/mrzigzag/testdir')
>>> os.path.split('/Users/mrzigzag/testdir')
('/Users/mrzigzag', 'testdir')
重命名os.rename(name.old, name.new)
,删除os.remove(file)
。
复制文件需要shutil
模块提供的copyfile()
函数。
利用Python的特性对文件目录进行批量操作,例如列出所有文件。
>>> [x for x in os.listdir('.') if not os.path.isdir(x)]
要列出所有.jpg
文件。
>>> [x for x in os.listdir('.') if x not os.path.isdir(x) and os.path.splitext(x)[1] == '.jpg']
3.序列化
把变量从内存中变成可存储或者可传输的过程称为序列化。反之称为反序列化。
Python提供两个模块来实现序列化,cPickle
和pickle
,功能一样,但是cPickle
用c写,速度快,一般优先调用。
try:
import cPickle as pickle
except ImportError:
import pickle
将对象写到文件。可以用pickle.dumps()
方法将任意对象序列化成为一个str,然后将该str写入到文件。或者用pickle.dump()
直接将对象序列化后写入到一个file-like Object。
with open('file_name', 'wb') as f:
pickle.dump(d, f)
读入同理,pickle.loads()
是从字符串读入,pickle.load()
则是从file-like Object直接读入。
Pickle的问题主要是保存的格式只适合Python,不同版本的Python可能都不兼容。如果有更加通用的序列化需求,用json
格式才是一个比较好的选择。
JSON是标准的Javascript对象,表示出来就是一个字符串,可以被所有语言读取,也方便存储和传输。
Python内置了json
模块提供了完善的Python对象到JSON格式的转换,内置的序列化函数和反序列化函数与pickle类似。
>>> import json
>>> d = dict(name='Bob', age=20, score=88)
>>> json.dumps(d)
'{"age": 20, "score": 88, "name": "Bob"}'
需要注意的一点是反序列化得到的所有字符串对象都是unicode
而不是str
,因为JSON标准规定的编码是UTF-8。
json
模块支持直接将Python的基本数据类型转换到JSON,对于自定义的类则需要用转换函数进行转换。
import json
class Student(object):
def __init__(self, name, age, score):
self.name = name
self.age = age
self.score = score
s = Student('Bob', 20, 88)
print(json.dumps(s))
运行会得到一个TypeError
,因为Student不是一个可序列化为JSON的对象。利用可选参数default
,写一个转换函数即可。
def studetn2dict(std):
return {
'name': std.name,
'age': std.age,
'score': std.score
}
print(json.dumps(s, default=student2dict))
遇到其他子类的实例时可能会无法转换,因而可以直接利用class中得__dict__
属性,它就是一个用来存储实例变量的dict
。
print(json.dumps(s, default=lambda obj: ojb.__dict__))
反序列化则是先用json.loads()
将JSON转换成一个dict
,然后传入object_hook
将dict转换成具体的实例。
def dict2student(d):
return Student(d['name'], d['age'], d['score])
json_str = '{"age": 20, "score": 88, "name": "Bob"}'
print(json.loads(json_str, object_hook=dict2student))
关于json.dumps()
方法提供的可选参数,可以参考这里。