前面介绍过文件类的操作,今天给大家更加全面深入的介绍
-
文件不存在时的写入:
你想像一个文件中写入数据,但是前提必须是这个文件在文件系统上不存在。 也就是不允许覆盖已存在的文件内容。
''' ========= ===============================================================
Character Meaning
--------- ---------------------------------------------------------------
'r' open for reading (default)
'w' open for writing, truncating the file first
'x' create a new file and open it for writing
'a' open for writing, appending to the end of the file if it exists
'b' binary mode
't' text mode (default)
'+' open a disk file for updating (reading and writing)
'U' universal newline mode (deprecated)
========= ==============================================================='''
f = open('/Users/darkmoon/PycharmProjects/untitled1/FileData/te.txt','x')
f.write("hello")
f.close()
-
字符串的I/O操作:
使用操作类文件对象的程序来操作文本或二进制字符串,从而模拟一个普通的文件,在单元测试中,你可以使用 StringIO
来创建一个包含测试数据的类文件对象, 这个对象可以被传给某个参数为普通文件对象的函数。
使用 io.StringIO()
和 io.BytesIO()
类来创建类文件对象操作字符串数据,相关操作跟文件的一模一样
s = io.StringIO("hello")
print(s.read())
s= io.BytesIO(b"hello")
print(s.read())
-
读写压缩文件
gzip
和 bz2
模块可以很容易的处理这些文件。当写入压缩数据时,可以使用 compresslevel
这个可选的关键字参数来指定一个压缩级别。
import bz2,gzip
with gzip.open("/Users/darkmoon/PycharmProjects/untitled1/FileData/test.gz",'w',compresslevel=3) as f:
f.write(b"hello")
with bz2.open("/Users/darkmoon/PycharmProjects/untitled1/FileData/test.bz2",'w',compresslevel=3) as f:
f.write(b"hello")
with gzip.open("/Users/darkmoon/PycharmProjects/untitled1/FileData/test.gz", 'r', compresslevel=3) as f:
print(f.read().decode("utf-8"))
with bz2.open("/Users/darkmoon/PycharmProjects/untitled1/FileData/test.bz2", 'r', compresslevel=3) as f:
print(f.read().decode("utf-8"))
最后一点, gzip.open()
和 bz2.open()
还有一个很少被知道的特性, 它们可以作用在一个已存在并以二进制模式打开的文件上。比如,下面代码是可行的:
import gzip
f = open('somefile.gz', 'rb')
with gzip.open(f, 'rt') as g:
text = g.read()
-
固定大小记录的文件迭代
你想在一个固定长度记录或者数据块的集合上迭代,而不是在一个文件中一行一行的迭代。可以使用 iter
和 functools.partial()
函数
import mmap, os,time
from functools import partial
if __name__ == "__main__":
filename = '/Users/darkmoon/PycharmProjects/untitled1/FileData/test.txt'
start=time.time()
with open(filename, 'rb') as f:
data = iter(partial(f.read, 10), b'')
end = time.time()
print(end-start)
-
读取二进制数据到可变缓冲区中
你想直接读取二进制数据到一个可变缓冲区中,而不需要做任何的中间复制操作。 或者你想原地修改数据并将它写回到一个文件中去。
if __name__ == "__main__":
with open("/Users/darkmoon/PycharmProjects/untitled1/FileData/te.txt",'rb') as f:
buf = bytearray(os.path.getsize("/Users/darkmoon/PycharmProjects/untitled1/FileData/te.txt"))
f.readinto(buf)
print(buf.decode("utf-8"))
readinto()
填充已存在的缓冲区而不是为新对象重新分配内存再返回它们。 因此,你可以使用它来避免大量的内存分配操作。
使用 f.readinto()
时需要注意的是,你必须检查它的返回值,也就是实际读取的字节数。
如果字节数小于缓冲区大小,表明数据被截断或者被破坏了(比如你期望每次读取指定数量的字节)。
另外有一个有趣特性就是 memoryview
, 它可以通过零复制的方式对已存在的缓冲区执行切片操作,甚至还能修改它的内容。比如:
import gzip,os
if __name__ == "__main__":
with open("/Users/darkmoon/PycharmProjects/untitled1/FileData/te.txt",'rb') as f:
buf = bytearray(os.path.getsize("/Users/darkmoon/PycharmProjects/untitled1/FileData/te.txt"))
f.readinto(buf)
print(buf.decode("utf-8"))
m = memoryview(buf)
m[2:3]=b'G'
print(buf.decode("utf-8"))
output:
hello
heGlo
-
内存映射的二进制文件
你想内存映射一个二进制文件到一个可变字节数组中,目的可能是为了随机访问它的内容或者是原地做些修改。
使用 mmap
模块来内存映射文件。
默认情况下, mmap.map()
函数打开的文件同时支持读和写操作。 任何的修改内容都会复制回原来的文件中。 如果需要只读的访问模式,可以给参数 access
赋值为 mmap.ACCESS_READ
。比如:
import mmap, os,time
from functools import partial
if __name__ == "__main__":
filename = '/Users/darkmoon/PycharmProjects/untitled1/FileData/test.txt'
start = time.time()
fd = os.open(filename, os.O_RDWR)
size = os.path.getsize(filename)
with mmap.mmap(fd, size, access=mmap.ACCESS_READ) as m:
while 1:
line = m.readline()
if line==b'':
break
end = time.time()
print(end-start)
为了随机访问文件的内容,使用 mmap
将文件映射到内存中是一个高效和优雅的方法。 例如,你无需打开一个文件并执行大量的 seek()
, read()
, write()
调用, 只需要简单的映射文件并使用切片操作访问数据即可。
需要强调的一点是,内存映射一个文件并不会导致整个文件被读取到内存中。 也就是说,文件并没有被复制到内存缓存或数组中。相反,操作系统仅仅为文件内容保留了一段虚拟内存。 当你访问文件的不同区域时,这些区域的内容才根据需要被读取并映射到内存区域中。 而那些从没被访问到的部分还是留在磁盘上。所有这些过程是透明的,在幕后完成!
-
创建临时文件和文件夹
tempfile
模块中有很多的函数可以完成这任务。 为了创建一个匿名的临时文件,可以使用 tempfile.TemporaryFile
:
from tempfile import TemporaryFile
with TemporaryFile('w') as f:
# Read/write to the file
f.write('Hello World\n')
f.write('Testing\n')
f.seek(0)
data = f.read()
TemporaryFile()
的第一个参数是文件模式,通常来讲文本模式使用 w+t
,二进制模式使用 w+b
。 这个模式同时支持读和写操作,在这里是很有用的,因为当你关闭文件去改变模式的时候,文件实际上已经不存在了。 TemporaryFile()
另外还支持跟内置的 open()
函数一样的参数.
为了创建一个临时目录,可以使用 tempfile.TemporaryDirectory()
。比如:
TemporaryFile()
、NamedTemporaryFile()
和 TemporaryDirectory()
函数 应该是处理临时文件目录的最简单的方式了,因为它们会自动处理所有的创建和清理步骤。 在一个更低的级别,你可以使用 mkstemp()
和 mkdtemp()
来创建临时文件和目录。
-
序列化Python对象
你需要将一个Python对象序列化为一个字节流,以便将它保存到一个文件、存储到数据库或者通过网络传输它。
对于序列化最普遍的做法就是使用 pickle
模块.之前做过介绍了就不介绍了。
注意:千万不要对不信任的数据使用pickle.load()。 pickle在加载时有一个副作用就是它会自动加载相应模块并构造实例对象。 但是某个坏人如果知道pickle的工作原理, 他就可以创建一个恶意的数据导致Python执行随意指定的系统命令。 因此,一定要保证pickle只在相互之间可以认证对方的解析器的内部使用。
有些类型的对象是不能被序列化的。这些通常是那些依赖外部系统状态的对象, 比如打开的文件,网络连接,线程,进程,栈帧等等。 用户自定义类可以通过提供 __getstate__()
和 __setstate__()
方法来绕过这些限制。 如果定义了这两个方法,pickle.dump()
就会调用 __getstate__()
获取序列化的对象。 类似的,__setstate__()
在反序列化时被调用。
import time
import threading
class Countdown:
def __init__(self, n):
self.n = n
self.thr = threading.Thread(target=self.run)
self.thr.daemon = True
self.thr.start()
def run(self):
while self.n > 0:
print('T-minus', self.n)
self.n -= 1
time.sleep(5)
def __getstate__(self):
return self.n
def __setstate__(self, n):
self.__init__(n)
c = Countdown(30)
f = open('/Users/darkmoon/PycharmProjects/untitled1/FileData/te.p', 'wb')
import pickle
pickle.dump(c,f)
f.close()
f = open('/Users/darkmoon/PycharmProjects/untitled1/FileData/te.p', 'rb')
d =pickle.load(f)
print(d)