Python中IO编程

文件操作

Python封装操作系统读写文件的API,用法与C兼容。进行文件读写的时候,由于操作磁盘上文件的行为都是由操作系统完成的,所以需要请求操作系统打开文件对象(也称之为文件描述符),接着通过操作系统提供的接口从文件对象中读取或写入数据。

读取文件

Python内置open()函数可以读取文件,如果文件不存在则抛出IOError

1
2
3
4
5
6
7
# r 表示 只读
>>> f = open('/User/1.txt', 'r')

# 如果文件不存在,则抛出错误,并说明出错原因
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
FileNotFoundError: [Errno 2] No such file or directory: '/User/1.txt'

如果文件打开成功,接着可以调用read()函数一次读取文件全部内容,并返回一个str对象。要记住:在文件使用完毕后必须要进行关闭

1
2
>>> f.read()
>>> f.close()

在进行文件操作的时候,都有可能产生IOError错误,我们也可以捕获这个错误:

1
2
3
4
5
6
7
8
try:
f = open('/User/1.txt', 'r')
print(f.read())
finally:
if f:
f.close()
except Exception as e:
print(e)

Python中使用with语句来简化调用形式,可以自动调用close()方法:

1
2
with open('/User/1.txt', 'r') as f:
print(f.read())

常用的读取文件的函数:

  • read()函数一次性读取文件全部内容
  • read(size)函数每次最多读取 size 个字节
  • readline()函数每次读取一行内容
  • readlines()函数一次读取所有内容并按行返回list

读取二进制文件:

1
2
3
4
5
# 读取视频、图片等二进制文件
>>> f = open('/Users/test.jpg', 'rb')
>>> f.read()

b'\x00\xd7...' # 十六进制

字符串编码:

open()函数还可以接受两个参数,第一个参数是将读取的字符编码进行编码,如果文件中出现非法编码的字符串,会抛出UnicodeDecodeError错误。这种情况,还可以接受第二个参数:errors。可以直接忽略编码出现的错误:

1
f = open('/Users/1.txt', 'r', encoding='gbk', errors='ignore')

写文件

写操作和读操作差不多,主要区别是open()函数的文件权限标示符不一致:wwb表示写文本文件或写二进制文件。当然,写操作完成一定要调用close()来关闭文件,也可以像上面那样使用with语句。

1
2
with open('/Users/test.txt', 'w') as f:
f.write('Hello, world!')

如果想写入特定编码的文本,可以给open()函数传入encoding参数将字符串转为指定编码。

读写内存中的文件

StringIO

在内当中读写字符串:

1
2
3
4
5
6
7
8
9
10
11
12
>>> from io import StringIO
>>> f = StringIO()
>>> f.write('hello')
5
>>> f.write(' ')
1
>>> f.write('world!')
6

# 获取写入的字符串
>>> print(f.getvalue())
hello world!

也可以用字符串初始化一个StringIO,然后,像读文件一样读取:

1
2
3
4
5
6
7
8
9
10
>>> from io import StringIO
>>> f = StringIO('Hello world!')
>>> while True:
... s = f.readline()
... if s == '':
... break
... print(s.strip())
...

Hello world!

BytesIO

读取内存中的二进制数据:

1
2
3
4
5
6
7
>>> from io import BytesIO
>>> f = BytesIO()
# 这里写入的是 utf-8 编码的二进制数据
>>> f.write('中文'.encode('utf-8'))
6
>>> print(f.getvalue())
b'\xe4\xb8\xad\xe6\x96\x87'

用bytes初始化BytesIO,然后读取它:

1
2
3
4
>>> from io import BytesIO
>>> f = BytesIO(b'\xe4\xb8\xad\xe6\x96\x87')
>>> f.read()
b'\xe4\xb8\xad\xe6\x96\x87'

操作文件

Python内置的os模块可以直接调用操作系统提供的接口,只需要import os模块:

1
2
3
4
5
6
7
8
9
>>> import os
# 操作系统类型:mac OS 是 posix,windows 是 nt
>>> os.name
# 获取系统信息,windows上没有
>>> os.uname()
# 获取操作系统环境变量
>>> os.environ
# 获取某个环境变量(PATH)的值
>>> os.environ.get('PATH')

操作文件和目录

操作文件和目录一般在osos.path模块中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 查看蛋清目录的绝对路径
# 查看当前目录的绝对路径:
>>> os.path.abspath('.')
'/Users/mac'
# 在某个目录下创建一个新目录,并返回完整路径,在合成两个路径的时候,推荐使用这种方式合成路径
>>> os.path.join('/Users/mac/Desktop', 'TestDir')
'/Users/mac/Desktop/TestDir'
# 在当前路径创建一个目录:
>>> os.mkdir('/Users/mac/Desktop/TestDir')
# 删掉一个目录:
>>> os.rmdir('/Users/mac/Desktop/TestDir')
# 拆分路径。最后一部分是最后级别的目录或者文件名(拆分、合并路径并不要求文件或者目录存在,只对字符串进行操作)
>>> os.path.split('/Users/mac/Desktop/TestDir')
('/Users/mac/Desktop', 'TestDir')
# 获取文件扩展名
>>> os.path.splitext('/path/to/1.txt')
('/path/to/1', '.txt')

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

序列化

Python中的pickle模块提供对象序列化功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
>>> import pickle
>>> d = dict(name='Tom', age=20)
# pickle.dumps() 方法将对象序列化成 bytes 。然后将这个 bytes 写入文件
>>> pickle.dumps(d)
b'\x80\x03}q\x00(X\x04\x00\x00\x00nameq\x01X\x03\x00\x00\x00Tomq\x02X\x03\x00\x00\x00ageq\x03K\x14u.'

# 或者直接使用 pickle.dump() 直接将对象序列化后写入 file-like Object
>>> f = open('/Users/mac/Desktop/dump.txt', 'wb')
>>> pickle.dump(d, f)
>>> f.close()

# 读取刚才写入的内容,读出文件的内容(bytes),用 pickle.loads() 函数反序列化成对象,也可以用 pickle.load() 方法从 file-like Object 反序列化对象
>>> f = open('/Users/mac/Desktop/dump.txt', 'rb')
>>> pickle.load(f)
{'name': 'Tom', 'age': 20}
>>> f.close()
# 注意:由于不同版本Python不兼容,因此这种方法只能保存不重要的数据。

在序列化 JSON 的时候,我们也可以这样做:

1
2
3
4
5
6
7
8
9
10
# 序列化 JSON
>>> import json
>>> d = dict(name='Tom', age=20)
>>> json.dumps(d)
'{"age": 20, "name": "Tom"}'

# 反序列化
>>> json_str = '{"age": 20, "name": "Tom"}'
>>> json.loads(json_str)
{'age': 20, 'name': 'Tom'}

dump()函数还接受不同的参数,来序列化自定义的对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import json

class Student(object):

def __init__(self, name, age):
self.name = name
self.age = age

s = Student('Tom', 20)
# 把任意 class 的实例变为 dict,通常每个 class 实例都有一个 __dict__ 属性,用来存储实例变量。
print(json.dumps(s, default=lambda obj: obj.__dict__))

# 同样也可以通过 loads() 方法将 json 反序列化成对象
def dict2Student(obj):
return Student(d['name'], d['age'])

>>> json_str = '{"age": 20, "name": "Tom"}'
>>> print(json.loads(json_str, object_hook=dict2Student))
<__main__.Student object at 0x10cd3c190>