使用 memoryview 读取二进制文件
Reading a binary file with memoryview
我在下面的代码中读取了一个大文件,它有一个特殊的结构——其中有两个块需要同时处理。我没有在文件中来回寻找,而是加载了包裹在 memoryview
调用中的这两个块
with open(abs_path, 'rb') as bsa_file:
# ...
# load the file record block to parse later
file_records_block = memoryview(bsa_file.read(file_records_block_size))
# load the file names block
file_names_block = memoryview(bsa_file.read(total_file_name_length))
# close the file
file_records_index = names_record_index = 0
for folder_record in folder_records:
name_size = struct.unpack_from('B', file_records_block, file_records_index)[0]
# discard null terminator below
folder_path = struct.unpack_from('%ds' % (name_size - 1),
file_records_block, file_records_index + 1)[0]
file_records_index += name_size + 1
for __ in xrange(folder_record.files_count):
file_name_len = 0
for b in file_names_block[names_record_index:]:
if b != '\x00': file_name_len += 1
else: break
file_name = unicode(struct.unpack_from('%ds' % file_name_len,
file_names_block,names_record_index)[0])
names_record_index += file_name_len + 1
文件已正确解析,但由于这是我第一次使用 mamoryview 界面,我不确定我是否正确解析。 file_names_block 由空终止的 c 字符串组成。
- 我的技巧
file_names_block[names_record_index:]
是使用 memoryview 魔法还是创建一些 n^2 切片?我需要在这里使用 islice
吗?
- 正如所见,我只是手动查找空字节,然后继续
unpack_from
。但是我在 How to split a byte string into separate bytes in python 中读到我可以在内存视图上使用 cast()
(文档?) - 使用它(或其他技巧)以字节为单位拆分视图的任何方式?我可以打电话给 split('\x00')
吗?这会保持内存效率吗?
我希望能了解一种正确的方法(在 python 2 中)。
A memoryview
在涉及到空终止字符串时不会给您带来任何优势,因为它们除了固定宽度的数据外没有任何设施。您也可以在这里使用 bytes.split()
:
file_names_block = bsa_file.read(total_file_name_length)
file_names = file_names_block.split(b'[=10=]')
切片 memoryview
不使用额外内存(视图参数除外),但如果使用强制转换,您会在尝试访问元素时为解析的内存区域生成新的本机对象顺序。
您仍然可以使用 memoryview
进行 file_records_block
解析;这些字符串以长度为前缀,让您有机会使用切片。在处理 folder_path
值时只保留内存视图的切片字节,不需要保留索引:
for folder_record in folder_records:
name_size = file_records_block[0] # first byte is the length, indexing gives the integer
folder_path = file_records_block[1:name_size].tobytes()
file_records_block = file_records_block[name_size + 1:] # skip the null
因为 memoryview
来自 bytes
对象,索引会给你一个字节的整数值,给定切片上的 .tobytes()
会给你一个新的 bytes
该部分的字符串,然后您可以继续切片以将剩余部分留给下一个循环。
我在下面的代码中读取了一个大文件,它有一个特殊的结构——其中有两个块需要同时处理。我没有在文件中来回寻找,而是加载了包裹在 memoryview
调用中的这两个块
with open(abs_path, 'rb') as bsa_file:
# ...
# load the file record block to parse later
file_records_block = memoryview(bsa_file.read(file_records_block_size))
# load the file names block
file_names_block = memoryview(bsa_file.read(total_file_name_length))
# close the file
file_records_index = names_record_index = 0
for folder_record in folder_records:
name_size = struct.unpack_from('B', file_records_block, file_records_index)[0]
# discard null terminator below
folder_path = struct.unpack_from('%ds' % (name_size - 1),
file_records_block, file_records_index + 1)[0]
file_records_index += name_size + 1
for __ in xrange(folder_record.files_count):
file_name_len = 0
for b in file_names_block[names_record_index:]:
if b != '\x00': file_name_len += 1
else: break
file_name = unicode(struct.unpack_from('%ds' % file_name_len,
file_names_block,names_record_index)[0])
names_record_index += file_name_len + 1
文件已正确解析,但由于这是我第一次使用 mamoryview 界面,我不确定我是否正确解析。 file_names_block 由空终止的 c 字符串组成。
- 我的技巧
file_names_block[names_record_index:]
是使用 memoryview 魔法还是创建一些 n^2 切片?我需要在这里使用islice
吗? - 正如所见,我只是手动查找空字节,然后继续
unpack_from
。但是我在 How to split a byte string into separate bytes in python 中读到我可以在内存视图上使用cast()
(文档?) - 使用它(或其他技巧)以字节为单位拆分视图的任何方式?我可以打电话给split('\x00')
吗?这会保持内存效率吗?
我希望能了解一种正确的方法(在 python 2 中)。
A memoryview
在涉及到空终止字符串时不会给您带来任何优势,因为它们除了固定宽度的数据外没有任何设施。您也可以在这里使用 bytes.split()
:
file_names_block = bsa_file.read(total_file_name_length)
file_names = file_names_block.split(b'[=10=]')
切片 memoryview
不使用额外内存(视图参数除外),但如果使用强制转换,您会在尝试访问元素时为解析的内存区域生成新的本机对象顺序。
您仍然可以使用 memoryview
进行 file_records_block
解析;这些字符串以长度为前缀,让您有机会使用切片。在处理 folder_path
值时只保留内存视图的切片字节,不需要保留索引:
for folder_record in folder_records:
name_size = file_records_block[0] # first byte is the length, indexing gives the integer
folder_path = file_records_block[1:name_size].tobytes()
file_records_block = file_records_block[name_size + 1:] # skip the null
因为 memoryview
来自 bytes
对象,索引会给你一个字节的整数值,给定切片上的 .tobytes()
会给你一个新的 bytes
该部分的字符串,然后您可以继续切片以将剩余部分留给下一个循环。