本系列是学习 廖雪峰 Python3 教程 过程中记录的笔记,本篇文章记录 Python 中的 I/O 编程,主要内容有文件的读写、内存的读写、对文件及目录的操作,以及两种常见的序列化操作。

I/O 编程

  • 基本概念:input , output , stream ;
  • 存在问题:输入和接收速度不匹配;
  • 解决方法:同步(等待 I/O 的执行结果)、异步(回调–好了叫我,轮询—好了没…好了没);
  • 收获新知:编程语言都会把操作系统提供的低级C接口封装起来方便使用;

文件读写

  • with open 的方式代码更加简洁,该方法会自动调用 f.close();
  • f.read() 会一次性读取文件的全部内容,如果不确定文件大小,建议反复调用 f.read(size) ,每次最多读取 size 个字节;
with open('/path/to/file', 'r') as f:
    print(f.read())
  • 对于二进制文件,打开的模式为 rb ;
  • 对于非 UTF-8 的文件,需要传入 encoding 参数;
  • 对于编码不规范的文件,可能会遇到非法编码的字符,最简单的处理方式是直接忽略;
with open('/Users/michael/gbk.txt', 'r', encoding='gbk', errors='ignore') as f:
    print(f.read())
  • 写文件:常用标识符 w 直写(会覆盖原文件的内容)、wb 直写二进制文件按、a 追加;

内存读写

  • StringIO 在内存中读写 str ;
  • BytestIO 在内存中读写 字节;
  • 内存写入的内容是否被及时读取、何时会被覆盖,都需要很仔细的查看;
from io import StringIO

f = StringIO("Hi! I'm Morton wang.")
print("f.getvalue():", f.getvalue())
f.write('hello')
f.write(' ')
f.write('world!')
print("f.getvalue():", f.getvalue())

output—————————————————————————
f.getvalue(): Hi! I'm Morton wang.
f.getvalue(): hello world!on wang.
from io import BytesIO

f = BytesIO()
f.write('Python大法好'.encode('utf-8'))
print("f.read():", f.read())
print("f.getvalue():", f.getvalue())
# 使用 'Python大法好' 的字节码初始化
f2 = BytesIO(b'Python\xe5\xa4\xa7\xe6\xb3\x95\xe5\xa5\xbd')
f2.write('中国'.encode('utf-8'))
print("f2.read():", f2.read().decode('utf-8'))
print("f2.getvalue():", f2.getvalue().decode('utf-8'))

output———————————————————————————
f.read(): b''
f.getvalue(): b'Python\xe5\xa4\xa7\xe6\xb3\x95\xe5\xa5\xbd'
f2.read(): 大法好
f2.getvalue(): 中国大法好

操作文件和目录

  • 获得系统类型( nt 表示 Windows)、指定的环境变量;
import os

print(os.name)
print(os.environ.get('CPATH'))

output————————————————————————
nt
C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.0\include
  • 目录部分:路径合并、拆分函数分别使用 os.path.join(path_1, path_2) os.path.split(path) ,好处就是可以避免不同操作系统的路径不同带来的麻烦,这些函数仅对字符串进行操作,不要求该路径一定存在;
  • 目录部分:os.path.isfile(path) os.path.isdir(path) 分别用来判断该路径是否是文件、目录;
  • 文件部分:文件重命名、删除文件;
import os

# 查看当前目录的绝对路径:
absolute_path = os.path.abspath('.')
print('absolute_path:\t', absolute_path)
# 在某个目录下创建一个新目录,首先把新目录的完整路径表示出来:
new_path = os.path.join(absolute_path, 'testdir')
print('new_path:\t\t', new_path)
# 然后创建一个目录:
os.mkdir(new_path)
# 删掉一个目录:
os.rmdir(new_path)

# os.path.split 后一部分是最后级别的目录或者文件名
print(os.path.split('/path/to/file.txt'))
print(os.path.split('/path/to/file'))
# os.path.splitext 可以获取文件的拓展名
print(os.path.splitext('/path/to/file.txt'))
print(os.path.splitext('/path/to/file'))

# 文件重命名
os.rename('test.txt', 'test.py')
# 文件删除
os.remove('test.py')

output————————————————————————
absolute_path:	 F:\wtlGit\python3_notes
new_path:		 F:\wtlGit\python3_notes\testdir
('/path/to', 'file.txt')
('/path/to', 'file')
('/path/to/file', '.txt')
('/path/to/file', '')

序列化

  • 把变量从内存中变成可存储或传输的过程称之为序列化,反过来就是反序列化;
  • 序列化为字节码及对应的反序列化:pickle.dumps(object)pickle.loads(dumps) ;
import pickle

student = dict(name='Bob', age=20, score=88)
student_dumps = pickle.dumps(student)
student_1 = pickle.loads(student_dumps)
print("student_dumps:", student_dumps)
print("student_1:\t", student_1)

output————————————————————
student_dumps: b'\x80\x03}q\x00(X\x04\x00\x00\x00nameq\x01X\x03\x00\x00\x00Bobq\x02X\x03\x00\x00\x00ageq\x03K\x14X\x05\x00\x00\x00scoreq\x04KXu.'
student_1:	 {'name': 'Bob', 'age': 20, 'score': 88}
  • 序列化到文件及对应的反序列化:pickle.dump(object, file)pickle.load(dump_file) ;
import pickle

student = dict(name='Bob', age=20, score=88)
with open('dump.txt', 'wb') as f:
    pickle.dump(student, f)

with open('dump.txt', 'rb') as f:
    student_1 = pickle.load(f)

print("student:\t", student)
print("student_1:\t", student_1)

output————————————————————
student:	 {'name': 'Bob', 'age': 20, 'score': 88}
student_1:	 {'name': 'Bob', 'age': 20, 'score': 88}
  • JSON 是序列化的标准格式,可以在不同的编程语言之间传递对象,并且比XML更快,而且可以直接在Web页面中读取,非常方便;
  • JSON 标准规定 JSON 编码是 UTF-8 ,表示出来就是一个字符串;
  • 和 pickle 的使用类似,JSON 也支持序列化到变量和文件,更多的参数使用 在这里
import json

class Student(object):
    def __init__(self, name, age, score):
        self.name = name
        self.age = age
        self.score = score

    def __str__(self):
        return "name:{} age:{} score:{}".format(self.name, self.age, self.score)

def student2dict(std):
    return {
        'name': std.name,
        'age': std.age,
        'score': std.score
    }

def dict2student(d):
    return Student(d['name'], d['age'], d['score'])

s = Student('Bob', 20, 88)
# json 不能直接序列化对象,需要将对象变为 Dict
s_json = json.dumps(s, default=student2dict)
print(s_json)
print(json.dumps(s, default=lambda obj: obj.__dict__))  # 效果同上
print(json.loads(s_json, object_hook=dict2student))     # object2str()

output——————————————————————————————————
{"name": "Bob", "age": 20, "score": 88}
{"name": "Bob", "age": 20, "score": 88}
name:Bob age:20 score:88