IO在计算机中指Input/Output,也就是输入和输出。由于程序和运行时数据是在内存中驻留,
由CPU这个超快的计算核心来执行,涉及到数据交换的地方,通常是磁盘、网络等,就需要IO接口。
通常,程序完成IO操作会有Input和Output两个数据流。当然也有只用一个的情况,
比如,从磁盘读取文件到内存,就只有Input操作,反过来,把数据写到磁盘文件里,就只是一个Output操作。
Stream(流)是一个很重要的概念,可以把流想象成一个水管,数据就是水管里的水,但是只能单向流动。
Input Stream就是数据从外面(磁盘、网络)流进内存,Output Stream就是数据从内存流到外面去。
由于CPU和内存的速度远远高于外设的速度,所以IO操作分两种方式:
- 第一种是CPU等着,也就是程序暂停执行后续代码,等待IO操作完成后再接着往下执行,这种模式称为同步IO;
- 另一种方法是CPU不等待,只是告诉磁盘”您老慢慢操作,我接着干别的事去了”,于是,后续代码可以立刻接着执行,这种模式称为异步IO。
很明显,使用异步IO来编写程序性能会远远高于同步IO,但是异步IO的缺点是编程模型复杂。
因为异步IO需要知道什么时候完成了,有很多种方式,比如回调模式,轮询模式等。
本篇的IO编程都是同步模式,异步IO单独一篇讲解。
文件读写
读写文件是最常见的IO操作,分为读取文本文件和读取二进制文件。我们演示基本使用方法:
1 2 3 4 5 6 7 8 9 10 11 12
| def basic_rw(): with open('/path/to/file', 'r', encoding='utf-8', errors='ignore') as f: print(f.read(200)) print(f.read()) with open('/Users/michael/test.txt', 'w', encoding='utf-8') as f: f.write('Hello, world!') with open('/Users/michael/test.jpg', 'rb') as f: f.read() with open('/Users/michael/test.jpg', 'rw') as f: f.write(b"hello world.")
|
StringIO和BytesIO
python动态语言有个很有用的特性就是鸭子类型,无需类型强制要求,只需要看起来像鸭子就认为是鸭子了。
对应到这里,就是在python中凡是定义了read()/write()方法的对象就成为file-like Object,
可像正常文件操作一样操作它们。比如典型的StringIO和BytesIO,都是在内存中读写,并不涉及真正的磁盘操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| def stringio_rw(): f = StringIO() f.write('hello') f.write(' ') f.write('中文!') print(f.getvalue())
f = StringIO('Hello!\nHi!\n中文!') while True: s = f.readline() if not s: break print(s.strip())
def bytesio_rw(): f = BytesIO() f.write('中文'.encode('utf-8')) print(f.getvalue())
|
文件和目录
如果我们要操作文件、目录,可以在命令行下面输入操作系统提供的各种命令来完成。比如ls、cp等命令。
Python内置的os模块也可以直接调用操作系统提供的接口函数。
操作文件和目录的函数一部分放在os模块中,一部分放在os.path模块中,这一点要注意一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| def file_dir(): print(os.path.abspath('.')) os.path.join('/Users/michael', 'testdir') os.mkdir('/Users/michael/testdir') os.rmdir('/Users/michael/testdir') os.path.split('/Users/michael/testdir/file.txt') os.path.splitext('/path/to/file.txt') os.rename('test.txt', 'test.py') os.remove('test.py') all_dir = [x for x in os.listdir('.') if os.path.isdir(x)] all_py = [x for x in os.listdir('.') if os.path.isfile(x) and os.path.splitext(x)[1] == '.py']
|
还有些功能比如复制操作cp在os模块中并未定义,不过我们可以通过shutil模块来达到目的。
shutil模块中找到很多实用函数,它们可以看做是os模块的补充
序列化
我们把变量从内存中变成可存储或传输的过程称之为序列化,在Python中叫pickling,
在其他语言中也被称之为serialization,marshalling,flattening等等,都是一个意思。
反过来,把变量内容从序列化的对象重新读到内存里称之为反序列化,即unpickling。
Python提供了pickle模块来实现序列化。
1 2 3 4 5 6 7 8 9 10 11 12
| def _pickle(): d = dict(name='Bob', age=20, score=88) result = pickle.dumps(d) dd = picle.loads(result) print(dd) with open('d:/dump.txt', 'wb') as f: pickle.dump(d, f) with open('d:/dump.txt', 'rb') as f: d = pickle.load(f) print(d)
|
如果我们要在不同的编程语言之间传递对象,就必须把对象序列化为标准格式,最好的方法是序列化为JSON
JSON表示的对象就是标准的JavaScript语言的对象,JSON和Python内置的数据类型对应如下:
JSON类型 |
Python类型 |
{} |
dict |
[] |
list |
“string” |
str |
1234.56 |
int或float |
true/false |
True/False |
null |
None |
Python内置的json模块提供了非常完善的Python对象到JSON格式的转换。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| def dict2student(d): return Student(d['name'], d['age'], d['score'])
class Student(object): def __init__(self, name, age, score): self.name = name self.age = age self.score = score
def _json(): """ import json d = dict(name='Bob', age=20, score=88) json.dumps(d) # 返回json字符串 with open('d:/dump.txt', 'w', encoding='utf-8') as f: json.dump(d, f) json_str = '{"age": 20, "score": 88, "name": "Bob"}' dd = json.loads(json_str) print(dd) with open('d:/dump.txt', 'r', encoding='utf-8') as f: ddd = json.load(f) print(ddd)
# 对象json序列化 s = Student('Bob', 20, 88) # 因为通常class的实例都有一个__dict__属性,它就是一个dict,用来存储实例变量 j_str = json.dumps(s, default=lambda obj: obj.__dict__) print(j_str) # 对象json反序列化 print(json.loads(j_str, object_hook=dict2student))
|
更高级和复杂的对象json序列化使用第三方模块jsonpickle: http://jsonpickle.github.io/
经常需要对文本按行处理,或做正则替换,搜索之类的,这时候用fileinput模块会很方便。
【基本格式】:
1
| fileinput.input([files[, inplace[, backup[, bufsize[, mode[, openhook]]]]]])
|
【默认参数】:
1
| fileinput.input(files=None, inplace=False, backup='', bufsize=0, mode='r', openhook=None)
|
【参数说明】:
1 2 3 4 5 6
| files: #文件的路径列表,默认是stdin方式,多文件['1.txt','2.txt',...] inplace: #是否将标准输出的结果写回文件,默认不取代 backup: #备份文件的扩展名,只指定扩展名,如.bak。如果该文件的备份文件已存在,则会自动覆盖。 bufsize: #缓冲区大小,默认为0,如果文件很大,可以修改此参数,一般默认即可 mode: #读写模式,默认为只读 openhook: #该钩子用于控制打开的所有文件,比如说编码方式等;
|
【常用函数】:
1 2 3 4 5 6 7
| fileinput.input() fileinput.filename() fileinput.lineno() fileinput.filelineno() fileinput.isfirstline() fileinput.isstdin() fileinput.close()
|
下面通过几个简单的例子来说明使用方法
实例1:利用fileinput读取一个文件所有行
1 2
| for line in fileinput.input('data.txt', openhook=fileinput.hook_encoded("utf-8")): print(line.rstrip())
|
实例2:读取文件名,行,内容
1 2 3
| for line in fileinput.input('data.txt'): print(fileinput.filename(), '|', 'Line Number:', fileinput.lineno(), '|: ', line)
|
实例3:利用fileinput对多文件操作,并原地修改内容
1 2 3 4 5 6
| def process(line): return line.rstrip() + ' line'
for line in fileinput.input(['1.txt', '2.txt'], inplace=1): print(process(line))
|
实例4:利用fileinput实现文件内容替换,并将原文件作备份
1 2 3 4 5
| for line in fileinput.input('data.txt', inplace=1): if line[-2:] == "\r\n": print(line.rstrip() + "\n") else: print(line.rstrip())
|
实例5:利用fileinput批处理文件
1 2 3 4
| for line in fileinput.input(glob.glob("d*.txt"), inplace=1): if fileinput.isfirstline(): print('-' * 20, 'Reading %s...' % fileinput.filename(), '-' * 20) print(str(fileinput.lineno()) + ': ' + line.rstrip().upper())
|
更多实例可以去我的github主页上面看..