IO在计算机中指Input/Output,也就是输入和输出。
由于程序和运行时数据是在内存中驻留,由CPU这个超快的计算核心来执行,涉及到数据交换的地方,通常是磁盘、网络等,就需要IO接口。
文件读写
Python内置了读写文件的函数。
读写文件就是请求操作系统打开一个文件对象(通常称为文件描述符),然后,通过操作系统提供的接口从这个文件对象中读取数据(读文件),或者把数据写入这个文件对象(写文件)。
读文件
>>> f = open('/Users/lizhengdao/PycharmProjects/learnPython3/mydict.py', 'r')
#1.打开一个文件,标识符’r’表示读
#打不开,会抛出IOError
>>> f.read() #2.读取文件全部内容
………
>>> f.read(size) #最多读取文件size个字节的文件内容
>>> f.readline() #每次读取一行内容
>>> f.readlines() #一次读取所有内容,并按行返回list
>>> f.close() #3.关闭文件
有时会出现IOError,导致无法正常close,可以使用try…finally来实现:
try:
f = open('/Users/lizhengdao/PycharmProjects/learnPython3/mydict.py', 'r')
f.read()
finally:
if f:
f.close()
#或者:
>>> with open('/Users/lizhengdao/PycharmProjects/learnPython3/mydict.py', 'r') as f:
print(f.read())
需要根据需求决定怎么用:
较小文件可以用read()一次读取;
不确定大小的文件,可以使用read(size)反复调用,比较保险;
配置文件,调用readlines()最方便:
for line in f.readlines():
print(line.strip()) #删掉末尾的'\n'
file-like Object
像open()函数返回的—— 有read()方法的 对象,还可以是内存的字节流,网络流,自定义流等等,在Python中统称为file-like Object.不要求从特定类继承。
二进制文件
要读取而机制文件(图片、视频等),用’rb’模式打开:
>>> f = open('/Users/Mojian/test.mp4', 'rb')
>>> f.read()
b'\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\…#十六进制表示的字节
字符编码
读取非UTF-8文件,需给open()函数传入encoding参数:
>>> with open('/Users/MOjian/desktop/测试文件/test.txt', 'r', encoding = 'gbk', errors = 'ignore') as f:
print(f.read())
当文件出现非法编码的字符,遇到这种情况,open()函数还接收一个errors参数,直接忽略。
写入文件
>>> with open('/Users/MOjian/desktop/测试文件/test.txt', 'w') as f:
f.write('Hello, world')
#'w’或'wb'(二进制打开)标识符表示写入内容,with语句在写入后自动close,但写入的内容会覆盖原内容
>>> with open('/Users/MOjian/desktop/测试文件/test.txt', 'a') as f:
f.write('Hello, world\n')
#标识符换成'a',添加文件时不会覆盖原内容;'\n'表示换行
StringIO和BytesIO
StringIO(在内存中读写str)
数据写入不一定是文件,也可以在内存中写入:
>>> from io import StringIO
>>> f = StringIO()
>>> f.write(' ')
1
>>> f.write('hello,world') #会覆盖原来的内容
12
>>> print(f.getvalue()) #获取写入后的str
hello,world
要读取StringIO,可以使用一个str初始化StringIO,然后,像文件一样读取:
>>> from io import StringIO
>>> f = StringIO('Hello\nworld\n!')
>>> f.getvalue()
'hello\nworld\n!' #只是获得写入后的str的内容,并不能像文件一样读取。
>>> f.strip() #strip()方法用于移除字符串头尾指定的字符(默认为空格)
#f是一个StringIO,并不是一个str字符串,故报错
Traceback (most recent call last):
File "<input>", line 1, in <module>
AttributeError: '_io.StringIO' object has no attribute 'strip'
>>> while True: #开始用str初始化StringIO
s = f.readline() #也可使用read属性,若使用getvalue则无限循环,不能使用readlines(为list)
if s == '':
break #当s='' 时终止循环
print(s.strip()) #输出str生成的新字符串
Hello
world
!
BytesIO
如果想在内存中读取二进制数据,需要使用BytesIO。
其实现了在内存中读写bytes,创建一个BytesIO,然后写入一些bytes:
>>> from io import BytesIO
>>> f = BytestIO()
>>> f.write('中文'.encode('utf-8')) #写入的不是str,而是经过utf-8编码的bytes
6
>>> print(f.getvalue())
b'\xe4\xb8\xad\xe6\x96\x87'
BytesIO同样可以使用bytes初始化,后可像文件一样读取:
>>> from io import BytesIO
>>> f = BytesIO('中文'.encode('utf-8'))
>>> f.read()
b'\xe4\xb8\xad\xe6\x96\x87'
stream position
针对运行中经常出现的返回空字符串问题的原因及解决方法:
>>> from io import StringIO
>>> f = StringIO('Hello World') #其stream position为0
>>> f.readline() #执行这句时,stream position移动到11
'Hello World'
>>> f.readline() #再次执行时,返回的是空字符串
''
>>> f.seek(0) #重新把stream position移动到0
>>> f.readline() #返回的字符串不为空
'Hello World'
操作文件和目录
Python内置的os模块可以直接调用操作系统提供的接口函数。
>>> import os
>>> os.name #获取当前系统类型
'pix' #为linux,Unix,Mac OS X,若为nt,则是windows
>>> os.uname() #获取系统的详细信息,不提供给windows使用
posix.uname_result(sysname='Darwin', nodename='MojianMacBook-Pro.local', release='16.6.0', version='Darwin Kernel Version 16.6.0: Fri Apr 14 16:21:16 PDT 2017; root:xnu-3789.60.24~6/RELEASE_X86_64', machine='x86_64')
环境变量
>>> os.environ #查看操作系统中定义的环境变量
environ({'SHELL': '/bin/bash', 'PYTHONIOENCODING': 'UTF-8', 'LOGNAME': ‘Mojian’...})
>>> os.environ.get('PATH') #获取某个变量的值get('key’)
'/Library/Frameworks/Python.framework/Versions/3.5/bin:./usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/Java/JavaVirtualMachines/jdk1.8.0_121.jdk/Contents/Home//bin:/usr/local/bin'
>>> os.environ.get('x','default')
'default'
操作文件和目录
>>> os.path.abspath('.') #查看当前目录的绝对路径
'/Users/Mojian'
>>> os.path.join('/Users/Mojian', 'testdir') #在某个目录下创建一个新的目录,首先把新目录的完整路径表达出来
'/Users/Mojian/testdir'
>>> os.split('/Users/Mojian/testdir')
'/Users/Mojian', 'testdir' #拆分路径
>>> os.mkdir('/Users/Mojian/testdir') #创建一个新目录
>>> os.rmdir('/Users/Mojian/testdir') #删除1个目录
>>> os.path.splitext('/Users/Mojian/test.png') #获取文件后缀名
'/Users/Mojian/test', '.png'
>>> os.rename('/Users/Mojian/test.png', 'test2.png') #重命名文件并移动当前目录
>>> os.remove('test2.png') #删除当前目录下的文件
shutil模块提供了copyfile()的函数,你还可以在shutil模块中找到很多实用函数
>>> [x for x in listdir('.') if os.path.isdir(x)]
['.git', '.idea', '__pycache__'] #获取当前目录下的所有目录
>>> [x for x in os.listdir('.') if os.path.isfile(x) and os.path.splitext(x)[1] == '.py']
#列出所有.py文件
练习
编写一个程序:可以在当前目录以及当前目录的所有子目录下查找文件名包含指定字符串的文件,并打印出相对路径
#/!usr/bin/env python3
# -*- encoding: utf-8 -*-
import os
def serch_file(pwd, name):
for x in os.listdir(pwd): #列出所有在pwd目录下的子目录和文件
abspathpwd = os.path.join(pwd, x)
if os.path.isfile(abspathpwd): #如果abspathpwd表示的路径为文件,则继续进行判断
if name in os.path.splitext(x)[0]: #判断字符串name在文件名x中([0]是为了排除后缀名),则输出绝对路径
print(abspathpwd)
if os.path.isdir(abspathpwd): #如果abspathpwd表示的路径为目录,则进行重新筛选
serch_file(abspathpwd, name)
pwd = os.path.abs('.') #定义筛选的目录为当前目录
name = input('serch file_name:') #要求输入搜索的字符串
序列化
把变量从内存中的内容变成可储存或传输的过程叫序列化。
>>> import pickle #导入pickle模块,来实现序列化
>>> d = dict(name = 'Bob', age = 20, score = 88) #定义一个dict
>>> e = pickle.dumps(d)
>>> e
b'\x80\x03}q\x00(X\x03\x00\x00\x00ageq\x01K\x14X\x05\x00\x00\x00scoreq\x02KXX\x04\x00\x00\x00nameq\x03X\x03\x00\x00\x00Bobq\x04u.'
pickle.dumps()方法可以把任意对象序列化为一个bytes,然后就可以把这个bytes写入文件了。
#另外一种pickle.dump()方法可以直接把对象序列化后直接写入file-like Object
>>> f = open('dump.txt', wb)
>>> pickle.dump(d, f)
>>> f.close()
把序列化过程反过来即:把文件对象从磁盘读到内存中,直接使用pickle.load()方法从一个file-like Object中直接反序列化出对象。
>>> import pickle
>>> with open('dump.txt', 'rb') as f:
… c = pickle.load(f)
>>> c
{'name':'Bob', 'age': 20, 'score': 88}
JSON
在不同语言之间传递对象,需要把对象序列化为标准格式——JSON。
JSON和Python内置的数据类型对应如下:
把Python对象转变成一个JSON:
>>> import json
>>> d = dict(name = 'Bob', age = 20, score = 90)
>>> json.dumps(d) #dumps返回一个str
"{"name":"Bob", "score": 90, "age": 20}"
把JSON反序列成一个Python对象,用loads()把字符串反序列化,或者对应的load()方法从file-like Object中读取字符串并反序列化:
>>> json_str = "{"name":"Bob", "score": 90, "age": 20}”
>>> json.loads(json_str)
{'age': 20, 'score': 90, name: 'Bob'}
JSON进阶
#很多时候,我们更喜欢用class表示对象,比如定义Student类,然后序列化:
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))
Traceback (most recent call last):
...
TypeError: <Student object at 0x103187780> is not JSON serializable#报错因为dumps方法 不知道如何将Student的实例变成一个JSON的{}对象
#转换函数,先把实例通过student2dict()函数转换成dict
def student2dict(self):
return {
'name':self.name,
'age':self.age,
'score':self.score
}
#可选参数default把任意一个对象变成一个可序列化成JSON的对象
#然后再序列化为JSON
print(json.dumps(s, default=student2dict))
#任意class的实例变成dict,class的实例都有__dict__属性,除定义了__slots__的class
print(json.dumps(s, default=lambda obj: obj.__dict__))
#把JSON反序列化为一个对象实例:
def dict2student(d):
return Student(d['name'], d['age'], d['score'])
json_str = '{"age": 20, "score": 88, "name": "Bob”}'
#用loads()方法首先转换出一个dict对象,传入object_hook函数把dict转换为实例
print(json.loads(json_str, object_hook = dict2student))