我认为对于具有一些 MATLAB 专业知识的人来说,解决方案将非常简单,但我不知道该怎么做。
我有一个二进制文件,我正在读取fread
,我正在读取该文件的前 4 个字节,然后是接下来的 2 个字节。
我基本上想要这个读取 4 个字节然后重复 2 个字节直到文件末尾的过程。
所以读取的字节数是4,2,4,2,4,2......
我有以下内容来读取第一对数据,我希望重复此操作。
fileID = fopen('MyBinaryFile');
4bytes = fread(fileID, 4);
fseek(fileID, 4, 0);
2bytes = fread(fileID, 2);
提前感谢您的任何帮助和建议
我认为这是您之前的问题 MATLAB 读取混合数据类型二进制文件的变体。
您的目标是读取包含混合数据类型的二进制文件。在您的情况下,它包含 2 列:
1x single
值(4 个字节(和 1x int16
个值(2 个字节(。
有几种方法可以读取此类文件。它们在速度上有所不同,因为某些方法可以最大程度地减少磁盘访问,但需要更多的临时内存,而其他方法仅使用所需的内存,但需要更多的磁盘访问(=较慢(。
最终,我将向您展示的 3 种方式会产生完全相同的结果。
这个问题的直接答案是下面的版本#3,但我鼓励你看看这里描述的其他两个选项,它们都非常值得理解。
出于示例的目的,我必须按照您的描述创建一个二进制文件。这是这样完成的:
%% // write example file
A = single(linspace(-3,1,11)) ; %// a few "float" (=single) data
B = int16(-5:5) ; %// a few "int16" data
fileID = fopen('testmixeddata.bin','w');
for il=1:11
fwrite(fileID,A(il),'single');
fwrite(fileID,B(il),'int16');
end
fclose(fileID);
这将创建一个 2 列二进制文件,列为:
- 11 个
float
类型的值从 -3 到 1。 - 11 个
int16
类型的值,从 -5 到 +5。
供将来参考:
>> disp(A)
-3.0000 -2.6000 -2.2000 -1.8000 -1.4000 -1.0000 -0.6000 -0.2000 0.2000 0.6000 1.0000
>> disp(B)
-5 -4 -3 -2 -1 0 1 2 3 4 5
在下面的每个解决方案中,第一列将在名为 varSingle
的变量中读取,第二列将在名为 varInt16
的变量中读取。
1(一次性读取所有数据 - 之后转换为正确的类型
%% // SOLUTION 1 (fastest) : Read all data in one go - convert to proper type after
fileID = fopen('testmixeddata.bin');
R = fread(fileID,'uint8=>uint8') ; %// read all values, most basic data type (unsigned 8 bit integer)
fclose(fileID);
colSize = [4 2] ; %// number of byte for each column [4 byte single, 2 byte int16]
R = reshape( R , sum(colSize) , [] ) ; %// reshape data into a matrix (6 is because 4+2byte=6 byte per column)
temp = R(1:4,:) ; %// extract data for first column into temporary variable (OPTIONAL)
varSingle = typecast( temp(:) , 'single' ) ; %// convert into "single/float"
temp = R(5:end,:) ; %// extract data for second column
varInt16 = typecast( temp(:) , 'int16' ) ; %// convert into "int16"
这是我最喜欢的方法。特别是为了速度,因为它最大限度地减少了磁盘上的读/寻操作,并且大多数后计算都是在内存中完成的(比磁盘操作快得多(。
请注意,我使用的临时变量只是为了清晰/冗长,如果您正确索引原始数据,则可以完全避免它。
要了解的关键是typecast
函数的使用。好消息是,自2014年以来,它变得更快b。
2( 逐列读取(使用"skipvalue"( - 2 遍方法
%% // SOLUTION 2 : Read column by column (using "skipvalue") - 2 pass approach
col1size = 4 ; %// size of data in column 1 (in [byte])
col2size = 2 ; %// size of data in column 2 (in [byte])
fileID = fopen('testmixeddata.bin');
varSingle = fread(fileID,'*single',col2size) ; %// read all "float" values, skipping all "int16"
fseek(fileID,col1size,'bof') ; %// rewind to beginning of column 2 at the top of the file
varInt16 = fread(fileID,'*int16',col1size) ; %// read all "int16" values, skipping all "float"
fclose(fileID);
那也行得通。它工作正常...但它会比上面的方法 1 慢,因为您必须扫描文件两次。如果文件非常大,并且上面的方法 1 由于out of memory
错误而失败,这可能是一个不错的选择。
3( 逐个元素读取
%% // SOLUTION 3 : Read element by element (slow - not recommended)
fileID = fopen('testmixeddata.bin');
varSingle=[];varInt16=[];
while ~feof(fileID)
try
varSingle(end+1) = fread(fileID, 1, '*single' ) ;
varInt16(end+1) = fread(fileID, 1, '*int16' ) ;
catch
disp('reached End Of File')
end
end
fclose(fileID);
这也确实有效,如果您正在编写C
代码,那就太好了。但是在 Matlab 中,这不是推荐的方式(您的最终选择(
正如所承诺的,上面的 3 种方法将为您提供我们在开头文件中编写的内容:
>> disp(varSingle)
-3.0000 -2.6000 -2.2000 -1.8000 -1.4000 -1.0000 -0.6000 -0.2000 0.2000 0.6000 1.0000
>> disp(varInt16)
-5 -4 -3 -2 -1 0 1 2 3 4 5
fileID = fopen('MyBinaryFile');
kk=1;
while ~feof(fileID)
bytes4(kk) = fread(fileID, 4);
fseek(fileID, 4, 0);
bytes2(kk) = fread(fileID, 2);
kk=kk+1;
end
while
循环条件是 ~feof
,代表文件结束。因此,只要您还没有到达文件的末尾,它就会运行。
我添加了kk
,以便您存储所有内容,而不仅仅是在每次循环迭代时覆盖它们。
在没有循环的情况下获取数据,有MATLABish的方法:
%'Sizes'
T = 4; %'Time record size'
D = 2; %'Date record size'
R = T+D; %'Record size'
%'Open file'
f = fopen('MyBinaryFile', 'rb');
if f < 0
error('Could not open file.');
end;
%'Read the entire file at once, and close file'
buf = fread(f, Inf, '*uint8');
fclose(f);
%'Ignore the last unpadded bytes, and reshape by the size of 1 record'
buf = reshape(buf(1:R*fix(numel(buf)/R)), R, []);
%'Pinpoint the data'
time_bytes = buf( 1: T, :);
date_bytes = buf(T+1:T+D, :);