【问题标题】:Reading a sparse array from a binary file on Matlab从 Matlab 上的二进制文件中读取稀疏数组
【发布时间】:2015-06-08 19:53:57
【问题描述】:

我必须编写一个函数,该函数从 binary file (.dat) 中读取一个二维数组,该数组位于一列中,其名称由函数的单个输入参数提供。该文件的格式如下: 首先,有两个 uint32 数字,分别对应数组的行数和列数。之后,有一个双数,它是数组上非零元素的数量。然后数组的每个非零元素在文件中由两个 uint32 标量和一个 double 标量按以下顺序表示:它的行索引 (uint32)、它的列索引 (uint32) 和它的值 (double)。一个例子可能是:

5
4
2
1
1
8
2
2
9

也就是说数组有5行4列,一共2个非零元素。这些元素将在位置(1,1)(值为8)和位置(2,2)(值为9)中找到。所有其他元素都等于 0。因此,数组将是:

8  0  0  0
0  9  0  0
0  0  0  0
0  0  0  0
0  0  0  0

函数必须返回它从文件中读取的二维数组作为输出参数,如果打开文件有问题,函数返回一个空数组。目前我已尝试使用此代码:

function A = sparse_array_in( filename )
fid = fopen( filename,'rt' );
if fid < 0
    A = [];
    return;
end
% Get total number of elements on the file
n = 0;
while (fgets(fid) ~= -1),
  n = n+1;
end
% Close then reopen
fclose(fid);
fid = fopen( filename,'rt' );
% Read size of array and number of non-zero elements
rows = fread( fid,1,'uint32' );
cols = fread( fid,1,'uint32' );
dims = [ rows,cols ];
non_zero = fread( fid,1,'uint32' );
% Create array of zeros
A = zeros( dims );   
% Fill array A with the values from the file
for i = 1:non_zero
    r = fread( fid,1,'uint32' );
    c = fread( fid,1,'uint32' );
    v = fread( fid,1,'double' );
    A(r,c) = v;
end
fclose( fid );
end

但它似乎不起作用。我错过了什么?

【问题讨论】:

  • 我要写函数,然后提交给老师提供的评分器,这个评分器告诉你这个函数是否正确,但它没有说什么是错误的。我已经尝试了几个小时,但它不起作用,我希望有人能帮我弄清楚我错过了什么,因为我看不到任何错误......
  • 您肯定需要创建自己的测试文件,因为您无权访问“grader”文件。从一个非常简单的开始,然后添加一些有明显错误的额外的,看看你的代码是否可以处理它们。

标签: arrays matlab


【解决方案1】:

您的代码存在一些问题:

  1. 如果文件 ID 为负数,您不仅应该返回空数组(正如您所做的那样),还应确保代码不会从该点开始。我会在分配给A 之后立即粘贴return 语句:

    if fid < 0
        A = [];
        return;
    end
    
  2. 接下来,当您确定文本文件中的行数时,请注意每次调用 fgets 时都在推进文件指针,因此当您最终完成读取所有行时,文件指针点在文本文件的末尾。对freadfgets 或任何从文件中读取内容的f* 家族的任何进一步调用都不会给您任何信息,因为您位于文件的末尾。您需要做的是关闭文件并再次打开它,以便您可以再次开始从文件中读取。因此,当您读取行数时关闭文件,然后再次打开它:

    % Get total number of elements on the file
    n = 0;
    while (fgets(fid) ~= -1),
      n = n+1;
    end 
    %// Close then reopen
    fclose(fid);
    fid = fopen( filename,'rt');
    
  3. 你没有使用fread 对。第二个参数告诉你有多少你想读入特定类型的数字。你使用的是 1、2 或 3,这意味着你正在读 1、2 或 3 个特定类型的数字.因为您使用 fread 读取每个呼叫的单个号码,所以它们应该全部为 1。此外,确定总行数对我来说似乎是多余的。如果给定非零元素的总数,为什么还要弄清楚有多少行?您已经知道有多少非零元素,因此只需从 1 迭代到尽可能多的非零数字。因此,试试这个:

    % Read size of array and number of non-zero elements
    rows = double(fread( fid,1,'uint32' )); %// Change
    cols = double(fread( fid,1,'uint32' )); %// Change
    dims = [ rows,cols ];
    non_zero = fread( fid,1,'uint32' ); %// Change
    % Create array of zeros
    A = zeros( dims );   
    % Fill array A with the values from the file
    for i = 1 : non_zero %// Change
        r = fread( fid,1,'uint32' ); %// Change
        c = fread( fid,1,'uint32' ); %// Change
        v = fread( fid,1,'double' ); %// Change
        A(r,c) = v;
    end
    %A = reshape( A,dims' ); %// Why are you reshaping?
    fclose( fid );
    

小评论:你为什么要重塑矩阵?如果您已经知道矩阵的行和列位置并且将它们准确地放在它们应该去的位置,为什么还要麻烦转置呢?


因此,使用上述 cmets,您的代码将如下所示:

function A = sparse_array_in( filename )
fid = fopen( filename,'rt' );
if fid < 0
    A = [];
    return; %// Change
end

% Read size of array and number of non-zero elements
rows = fread( fid,1,'uint32'); %// Change
cols = fread( fid,1,'uint32'); %// Change
dims = [ rows,cols ];
non_zero = fread( fid,1,'uint32' ); %// Change
% Create array of zeros
A = zeros( dims );   
% Fill array A with the values from the file
for i = 1:non_zero
    r = fread( fid,1,'uint32' ); %// Change
    c = fread( fid,1,'uint32' ); %// Change
    v = fread( fid,1,'double' ); %// Change
    A(r,c) = v;
end
%// Change - remove reshape
fclose( fid );
end

这是一个示例,它表明它适用于二进制文件。我创建了以下场景:

5
7
4
1
1
1
2
2
2
3
3
3
4
4
4

这是一个 5 x 7 矩阵,其中 (1,1) = 1, (2,2) = 2, (3,3) = 3, (4,4) = 4 具有 4 个非零值。我创建了一个二进制文件,然后使用我上面修复的函数来获取结果:

fid = fopen('sparse_binary.dat', 'w');
fwrite(fid, 5, 'uint32');
fwrite(fid, 7, 'uint32');
fwrite(fid, 4, 'uint32');
fwrite(fid, 1, 'uint32');
fwrite(fid, 1, 'uint32');
fwrite(fid, 1, 'double');
fwrite(fid, 2, 'uint32');
fwrite(fid, 2, 'uint32');
fwrite(fid, 2, 'double');
fwrite(fid, 3, 'uint32');
fwrite(fid, 3, 'uint32');
fwrite(fid, 3, 'double');
fwrite(fid, 4, 'uint32');
fwrite(fid, 4, 'uint32');
fwrite(fid, 4, 'double');
fclose(fid);
A = sparse_array_in('sparse_binary.dat');

我得到A

A =

     1     0     0     0     0     0     0
     0     2     0     0     0     0     0
     0     0     3     0     0     0     0
     0     0     0     4     0     0     0
     0     0     0     0     0     0     0

...这是我们所期望的。

【讨论】:

  • 我明白你做了什么,但评分员说它仍然不正确。我相信一个问题可能是评分者传递了文件,例如:5 7 4 1 1 1 2 2 2 3 3 3 4 4 4,或者甚至可能是列向量。这可能是导致您的解决方案出现错误的原因吗?
  • @Hec46 - 我需要知道评分者传递数据的确切方式,否则我永远不会正确。我假设文本文件是空格分隔和回车分隔的。
  • @Hec46 - 您的帖子中也没有明确说明数据的格式。请更新它,以便我们准确了解文件的结构。
  • 对不起,让我们看看我是否能更好地解释一下。在第一个任务中,我被要求编写一个函数,该函数给出一个数组和一个文件名,然后将该数组写入一个具有我在帖子中解释的结构的二进制文件。测试后,我发现它是在列向量上执行的。然后,这个.dat 文件可以传递给第二个函数,它应该返回与我传递给第一个函数的完全相同的数组。老师刚刚告诉我,为了比较结果,我需要使用 isequal() 并获得 1 作为结果。我希望这能澄清一点,我现在将编辑帖子。谢谢!
  • @Hec46 - 啊,所以如果它是二进制的,那么您使用fread 是合理的。不过我会等到你更新你的帖子。
猜你喜欢
  • 2015-08-23
  • 2013-11-05
  • 2013-09-09
  • 1970-01-01
  • 1970-01-01
  • 2011-03-25
  • 1970-01-01
  • 1970-01-01
  • 2011-12-24
相关资源
最近更新 更多