你发现了虫洞 这里查看效果更好
5.8固定大小记录文件迭代
问题:想在一个固定长度记录或者数据块的集合上迭代,而不是在一个文件中一行一行的迭代 使用iter和functools.partial() 说明:iter()可以接受两个参数,一个可迭代对象、一个标记值,它会创建一个迭代器,并一直调用可调用对象直到返回标记值为止
from functools import partial
RECORD_SIZE = 30
with open('data_file/test1_3.txt' ,'rb' ) as f:
records = iter(partial(f.read,RECORD_SIZE),b'' )
for r in records:
print(r)
b'my name is flfl\nlove python \ns'
b'ay hello\n to the world\n python'
b' nihao \n'
5.9读取二进制文件到可变缓冲区
问题:如何读取二进制数据到一个可变缓冲区,不需要任何中间复制操作,或者想原地修改数据并将它写入到一个文件中 方案:为了读取数据到一个可变数组中,使用文件对象的readinto()方法
import os.path
def read_into_buffer (filename) :
buf = bytearray(os.path.getsize(filename))
with open(filename,'rb' ) as f:
f.readinto(buf)
return buf
with open('data_file/test5_9.bin' ,'wb' ) as f:
f.write(b'hello world' )
buf = read_into_buffer('data_file/test5_9.bin' )
buf
bytearray(b'hello world')
with open('data_file/test5_09.bin' ,'wb' ) as f:
f.write(buf)
readinto()用来填充已经存在的缓冲区而不是为新的对象重新分配内存再返回它们。 可以使用它来避免大量的内存分配操作,比如,读取一个由相同大小的记录组成的二进制文件时,可以按下面的方式写:
record_size = 32
buf = bytearray(record_size)
with open('somefile' ,'rb' ) as f:
while True :
n = f.readinto(buf)
if n < record_size:
break
pass
另外,一个有趣的特性就是memoryview,他可以通过零复制的方式对已经存在的缓冲区执行切片操作,甚至还能修改内容
with open('data_file/test5_9.bin' ,'wb' ) as f:
f.write(b'hello world' )
buf = read_into_buffer('data_file/test5_9.bin' )
buf
bytearray(b'hello world')
m1 = memoryview(buf)
m2 = m1[-5 :]
m2
<memory at 0x0138B238>
m2[:] = b'WoRld'
buf
bytearray(b'hello WoRld')
5.10内存映射的二进制文件
问题:如何将一个二进制文件映射到一个可变字节数组中,以便可以随机访问或者原地修改 方案:使用mmap模块来映射文件
import os
import mmap
def memory_map (filename,access = mmap.ACCESS_WRITE) :
size = os.path.getsize(filename)
fd = os.open(filename,os.O_RDWR)
return mmap.mmap(fd,size,access=access)
size = 100
with open('data_file/data' ,'wb' ) as f:
f.seek(size-1 )
f.write(b'\x00' )
m = memory_map('data_file/data' )
len(m)
100
m[0 :10 ]
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
m[0 ]
0
m[0 :11 ] = b'hello world'
m.close()
with open('data_file/data' ,'rb' ) as f:
print(f.read(11 ))
b'hello world'
mmap()返回的对象同样可以作为一个上下文管理器来使用,这时候底层的文件会被自动的关闭
with memory_map('data_file/data' ) as m:
print(len(m))
print(m[0 :11 ])
100
b'hello world'
m.closed
True
默认情况下,memory_map()函数打开文件的同时支持读写操作,任何修改内容都会反映到原来的文件中。如果只是想读取,可以给access参数传递mmap.ACCESS_READ
m = memory_map('data_file/data' ,mmap.ACCESS_READ)
m[0 ]= b'1'
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-25-0a339ef9efd0> in <module>()
----> 1 m[0]= b'1'
TypeError: mmap can't modify a readonly memory map.
如果你想修改本地数据,但是不想将修改反映到原始文件中,可以使用mmap.ACCESS_COPY
m = memory_map('data_file/data' ,mmap.ACCESS_COPY)
m[10 :21 ] = b'hello world'
m[10 :21 ]
b'hello world'
m = memory_map('data_file/data' ,mmap.ACCESS_COPY)
m[10 :21 ]
b'd\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
为了最忌访问一个文件,将其映射到内存是一个高效的做法,无需打开文件执行seek(),write(),read()等操作,只需要使用切片即可 一般来说,mmap()所暴露的内存看上去只是一个二进制数组对象,但是可以使用一个内存视图来解析数据
m = memory_map('data_file/data' )
v = memoryview(m).cast('I' )
v[0 ] = 7
m[:4 ]
b'\x07\x00\x00\x00'
5.11文件路径名的操作
问题:需要使用路径名获取文件名,绝对路径 方案:使用os.path
import os
path = '/Users/beazly/Data/data.csv'
os.path.basename(path)
'data.csv'
os.path.dirname(path)
'/Users/beazly/Data'
os.path.join('tmp' ,'data' ,os.path.basename(path))
'tmp\\data\\data.csv'
os.path.splitext(path)
('/Users/beazly/Data/data', '.csv')
5.12测试文件是否存在
问题:如何直到某个文件是否存在 方案:os.path.exists()
import os
os.path.exists('data_file/test1_3.txt' )
True
os.path.exists('data_file/data1' )
False
os.path.isfile('data_file/data' )
True
os.path.islink('data_file/data' )
False
os.path.getsize('data_file/data' )
100
os.path.getmtime('data_file/data' )
1536028339.0008936
import time
time.ctime(os.path.getmtime('data_file/data' ))
'Tue Sep 4 10:32:19 2018'
5.13获取文件夹中的文件列表
问题:如何获取某个文件夹中的文件列表 方案:os.listdir()
import os
names = os.listdir('data_file' )
names[:4 ]
['.ipynb_checkpoints', 'ch2_3_test', 'ch4_02.txt', 'ch5_1.txt']
如果想要过滤一些文件,可以使用os.path中的一些函数利用列表推导
import os
names = [name for name in os.listdir('data_file' )
if os.path.isfile(os.path.join('data_file' ,name))]
names[:4 ]
['ch2_3_test', 'ch4_02.txt', 'ch5_1.txt', 'ch5_2.txt']
字符出的startswith()和endswith()对于过滤目录内容也很有用
txt_file = [name for name in os.listdir('data_file' )
if name.endswith('.txt' )]
txt_file
['ch4_02.txt',
'ch5_1.txt',
'ch5_2.txt',
'ch5_3.txt',
'somefile.txt',
'test1_3.txt']
对于文件名的匹配还可以使用glob和fnmatch
import glob
txt_files = glob.glob('data_file/*.txt' )
txt_files
['data_file\\ch4_02.txt',
'data_file\\ch5_1.txt',
'data_file\\ch5_2.txt',
'data_file\\ch5_3.txt',
'data_file\\somefile.txt',
'data_file\\test1_3.txt']
from fnmatch import fnmatch
txt_files = [name for name in os.listdir('data_file' )
if fnmatch(name,'*.txt' )]
txt_files
['ch4_02.txt',
'ch5_1.txt',
'ch5_2.txt',
'ch5_3.txt',
'somefile.txt',
'test1_3.txt']
如果还想获取其它更加具体的文件信息,可以使用os.stat()
import os
import os.path
import glob
txt_files = glob.glob('data_file/*.txt' )
name_size = [(name,os.path.getsize(name),os.path.getmtime(name))
for name in txt_files]
for name,size,mtime in name_size:
print(name,':' ,size,':' ,mtime)
data_file\ch4_02.txt : 111 : 1535855480.2224486
data_file\ch5_1.txt : 26 : 1535961411.2705722
data_file\ch5_2.txt : 7 : 1535964523.1387627
data_file\ch5_3.txt : 3 : 1535962989.8271592
data_file\somefile.txt : 5 : 1535977584.2078972
data_file\test1_3.txt : 68 : 1535781448.546163
file_data = [(name,os.stat(name))for name in txt_files]
for name, meta in file_data:
print(name,' ' ,meta.st_size,' ' ,meta.st_mtime)
data_file\ch4_02.txt 111 1535855480.2224486
data_file\ch5_1.txt 26 1535961411.2705722
data_file\ch5_2.txt 7 1535964523.1387627
data_file\ch5_3.txt 3 1535962989.8271592
data_file\somefile.txt 5 1535977584.2078972
data_file\test1_3.txt 68 1535781448.546163