将矢量创建的 Matlab 代码翻译成 C++

Translating Matlab code of vector creation to C++

我有这个 Matlab 代码:

[arr1 arr2 arr3] = fReadFileBin(filename));

函数体是:

function [Result1 , Result2 , Result3 ] = fReadFileBin(filename)
    fid = fopen(filename, 'r');
    fseek(fid, 180, 0);
    PV = fread(fid, [A*3 B+2], 'float32');
    fclose(fid);

    Result1 = PV(1:3:3*A, 2:B+1);
    Result1 = Result1';
    Result2 = PV(2:3:3*A, 2:B+1);
    Result2 = Result2';
    Result3 = PV(3:3:3*A, 2:B+1);
    Result3 = Result3';

结果我有 3 个填充向量,大小为 BxA,类型为 Double。

当我尝试用 C++ 重写它时:

   std::vector<std::vector<double>> result;
   result.resize(B, std::vector<double>(A));    

   std::ifstream is(filename, std::ios::binary);

   is.seekg(0, std::ios_base::end);
   std::size_t size = is.tellg();
   is.seekg(0, std::ios_base::beg);

   is.seekg( 180, 0);

   std::vector<double> PV (size / sizeof(double));
   if (!is.read((char*)&PV[0], size))
   {
       throw std::runtime_error("error reading file");
   }
   // Load the data
   is.read((char*)&PV[0], size);
   is.close();

   // std::vector<double> Result1 = 
   // std::vector<double> Result2 = 
   // std::vector<double> Result3 = 

   //R=R'
   //R[j][i] = R[i][j];  

对我来说确实有意义,但仍然不明白如何重写这部分:(1:3:3*A, 2:B+1) in C++ ?

备注:
-我仅限于使用标准库(没有 boost、mmap 等)。
-我检查了有关冒号的 Mathwork 文档(但仍然无法理解如何实现它)。

由于向量的结果大小是固定的,我宁愿使用 std::array:

std::array<std::vector<double>, 3> result;

也不再调整大小,反正看起来会简单得多:

//result.resize(B, std::vector<double>(A));
result.resize(3);

通过这一行,您的外部向量现在恰好包含三个向量 - 每个向量都是空的 - 就像数组方法一样。无论你最终 select 是哪个,你都需要明确地调整内部向量的大小。不过,我们稍后会再谈这个。

is.seekg(0, std::ios_base::end);
std::size_t size = is.tellg();  // OK, you fetched file size

//is.seekg(0, std::ios_base::beg); // using beg, you can give the desired offset directly
//is.seekg( 180, 0);  // but use the seekdir enum! So:
is.seekg(180, std::ios_base::beg);

但是,您应该检查之前至少有 180 字节的文件。您应该知道这些操作中的 any 可能会失败,因此您应该检查流的状态,在每个单个操作之后或至少在其中几个操作之后(因此至少在调整大小之前你的向量 PV)。旁注:如果流已经处于失败状态,则每个后续操作也将失败,除非您 clear() 之前的错误状态。

std::vector<double> PV (size / sizeof(double));

呃,我觉得很奇怪...你从偏移量 180 开始,所以我认为你应该在除法之前减去;一世。即:

size_t size = ...;
if(size < 180) // bad file!
{
    // throw or whatever appropriate
}
size -= 180;
// go on...

如果没有此修复,下一行将 总是 导致抛出以下异常,因为您会读取超出文件末尾的内容(请记住,您是从文件开始读取的偏移 180!):

if (!is.read((char*)&PV[0], size))

但更喜欢 C++ 风格的转换:

if (!is.read(reinterpret_cast<char*>(PV.data()), size))

您会很快发现您需要 reinterpret_cast,有时是合适的,但在大多数情况下,如果您考虑使用它,至少应该敲响警钟,它只是隐藏了一些更深层次的问题,例如未定义的行为。 PV.data() 自 C++11 以来就存在并且比 &PV[0] 更容易阅读,但两者是等价的。

但是,我们现在还有一个不同的问题:

虽然该标准没有说明任何关于精度甚至格式的信息 ("The value representation of floating-point types is implementation-defined."),但在您的系统上,double 很可能是 64 位 IEEE754 数据类型。您真的确定数据正是以这种格式存储的吗?只有这样,这个 才能 工作,但是,它是非常危险的,文件生产者和消费者可能会说不同的语言,并且很可能你得到错误的输入...

现在承认,我根本不是matlab专家,但你的下面这行让我强烈怀疑上面的输入格式是否适用:

PV = fread(fid, [A*3 B+2], 'float32');
                                 ^^

最后,你已经读取你的数据已经在if子句中,所以放弃这个第二行,它只会产生另一个失败...

如果现在数据不是以二进制形式存储,而是以人类可读的格式存储,您可以读取如下值:

std::vector<double> pv; // prefer lower camel case variable names
pv.reserve(size/sizeof(double)); // just using as a size hint;
                                 // we can't deduce number of entries
                                 // from file length exactly any more

double v;
while(is >> v)
{
    pv.push_back(v);
}
if(!v.eof())
{
    // we did not consume the whole file, so we must
    // assume that some input error occurred!
    // -> appropriate error handling (throw?)
}

慢慢走到尽头:

// std::vector<double> Result1 = 
// std::vector<double> Result2 = 
// std::vector<double> Result3 = 

注释掉;对,你不需要它们,你已经在结果 vector/array 中有了它们,我。 e. result[0]result[1]result[2]...

根据需要调整它们的大小(或保留)以将结果数据放入并继续。

很抱歉,我现在不太清楚你的 matlab 计算是做什么的,我也不会为了这个答案而学习 matlab——不过,有了上面的提示,你可能会相处得很好。进一步提示:您不能将 vectors/arrays 作为一个整体相互相乘或直接与标量相乘;您必须在循环中分别为每个元素执行此操作。您可能会考虑 std::valarray an interesting alternative, though. Additionally, you might find some interesting stuff in the algorithm library,尤其是在 "numeric operations" 部分下。如果您对这些不满意,请随时提出另一个问题...