三、I/O操作

文件读写

文件读写功能是由操作系统提供的,现代操作系统不允许一般的程序直接操作磁盘,一般的程序的读写操作实际上是请求操作系统打开一个文件对象(通常称为文件描述),然后,通过操作系统提供的接口从这个文件对象中读取数据(读文件),或者把数据写入这个文件对象。

1
2
with open('/path/to/file', 'r') as file:
print(file.read())

调用read()会一次性读取文件的所有内容,需要注意文件的大小,以免内存被爆掉,保险起见,可以使用read(szie)进行读取size大小的文件内容,然后反复调用,此外,readline()可以一次读取一行的内容,readlines()一次读取所有的内容,并按行返回一个list

二进制文件

open()默认都是读取文本文件,并且是UTF-8编码的文本文件,要读取二进制文件,比如图片、视频之类的文件,可以设置open()的参数为rb模式即可。

字符编码

要读取非UTF-8编码的文本文件,需要给open() 函数传入encoding参数,比如读取GBK编码的文件,遇到不规范的文件,可能会导致UnicodeDecodeError的异常,可以再open()函数中添加error参数,最简单的解决方式是直接忽略。

1
GBK_file = open('path/to/gbk_file', 'r', encoding='gbk', errors=ignore)

写文件

写文件和读文件一样都是open()方法,区别是写文件的传入标识符为:w或者wb来写入文本文件或者二进制文件。需要注意的是在调用完open()函数之后务必调用f.close()函数进行关闭文件,原因是当我们写文件的时候,操作系统不会立刻将数据写入磁盘,而是将数据缓存到内存,事件空闲的时候再进行慢慢写入,只有调用close()方法时,操作系统才会保证将没有写完的数据写到磁盘。

w方式写入文件时,如果文件存在时,将会直接覆盖原有的文件(相当于删掉后直接新写入一个文件)如果想在文件末尾追加新的内容,可以传入a以追加模式写入。

StringIO 和 BytesIO

StringIO

有时候数据读写不一定是文件,也可能是内存读写,要在内存中读写str需要将str写入大StringIO,先创建一个StringIO,然后想文件一样写入即可。

1
2
3
4
5
6
7
8
9
10
from io import StringIO
# 文件写入
memory_file = StringIO()
memory_file.write('hello world')
#文件读取
while True:
read_file = memory_file.readline()
if read_file == '':
break
print(read_file.strip())

BytesIO

BytesIO实现了在内存中读取二进制数据

1
2
3
4
5
from io import BytesIO
byte_file = BytesIO()
byte_file.wirte('中文', encode('utf-8'))
# 通过getvalue()来查看输入的内容
print(byte_file.getvalue())

操作文件和目录

os模块

os.name可以查看操作系统类型,如果输出是posix,说明操作系统是Linux , Unix或者Mac OS X,如果是nt则就是Windows系统。在posix系统下使用os.uname()可以查看系统的详细信息,但是在Windows下面没有这模块功能。

环境变量

os模块中的os.environ可以查看系统存放在该变量中定义的环境变量,要获取某个变量的值,可以使用os.environ.get('environ_key'),比如:

1
2
import os
print(os.environ.get('PATH'))

操作文件和目录*
操作文件和目录的函数的一部分在os模块中,一部分存放在os.path模块当中。

查看、创建、删除目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    import os
os.path.abspath('.') # 查看当前目录的绝对路径
os.path.join('/Users/mike', 'test.txt') # 路径拼接(这个非常有用)/Users/mike/test.txt
os.mkdir('/Users/testdir') # 创建新的目录testdir
os.rmdir('/Users/testdir') # 删除testdir文件夹
```

**把两个路径进行拼接的时候,不建议直接进行字符串拼接,通过`os.path.join()`能够正确的处理不同操作系统的路径,同样要拆分路径时,也不要直接去拆字符串,而要通过`os.path.split()`函数,这样可以将一个路径拆分成两个部分,最后一个部分总是最后级别的目录或文件名。`os.path.splitext()`可以直接得到文件扩展名(这个在文件类型判断的时候非常有用)**

#### 文件操作

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

shutil模块里面有很多实用的操作系统级别的模块。
更多Python文件操作方法:Python3 OS模块

序列化

程序运行过程中,所有的变量都是在内存中,一旦程序结束,系统就会回收变量所占用的内存。而将内存中的变量变成可存储或者传输的过程称之为序列化。Python中的序列化模块是pickle,要使得序列化更加通用可以使用json模块。

1
2
3
4
5
6
7
8
9
import pickle
info = dict(name='胖子大叔', age=18)
pickle.dump(info) # 打印出序列化的信息,熟悉PHP的同学对dump()肯定不会陌生
save_file = open('dump.txt', 'wb') # 序列化的信息是二进制的,所以采用二进制标识符进行写入操作
save_file.close() # 文件操作完毕,执行关闭文件操作,这是一个好的习惯
read_file = open('dump.txt', 'rb') # 读取文件
info = pickle.load(read_file)
read_file.close()
print(info) # 输出可以看出,保存的信息又回来了

需要注意的是pickle只适用于Python,并且不同版本的Python还可能不兼容(这个就比较坑了)。如果想要很好的兼容性,建议使用json进行序列化保存

JSON

JSON表示的对象是JavaScript语言的对象

import json
info = dict(name='胖子大叔', age=18)
info2json = json.dumps(info) 
print(info2json) # 输出json格式:{"age":18, "name":"胖子大叔"}
print(type(info2json)) # <class 'str'>
json2info = json.loads(info2json)
print(json2info) # 输出dict格式:{'age':18, 'name':'胖子大叔'}
print(type(json2info)) # <class 'dict'>

Json详细参考:Json.dumps