【问题标题】:What is a good way of dynamically expanding an array to import data?动态扩展数组以导入数据的好方法是什么?
【发布时间】:2015-02-16 14:40:00
【问题描述】:

我正在寻求有关创建动态扩展数组以从 .csv 文件导入数据的帮助。我不想查看文件有多大并在源代码中编辑变量/提示用户,我只想导入数据然后以各种方式进行操作。首先,我的代码原样:

#include <fstream>
#include <sstream>
#include <iostream>

int main()
{

//declare variables and arrays
long rows = 170260;
int cols = 5;
double **rawData = new double*[rows]; //on heap because of size
for(long pi = 0; pi < rows; ++pi) //create an array of pointers
{
         rawData[pi] = new double[cols];
}
char buff[200];
double deltaT;
double carDeltaV;
double *carV = new double[rows]; //on heap because of size

//import raw data
std::cout << "Importing filedata.csv...";

std::ifstream rawInput("filedata.csv");

for(long r = 0; r < rows; ++r)
{
      rawInput.getline(buff, 200);
      std::stringstream ss(buff);

      for(int c = 0; c < cols; ++c) 
      {
            ss.getline(buff, 40, ',');
            rawData[r][c] = atof(buff);
      }
}

std::cout << "Done." << std::endl;

//create speed matrix
carV[0] = 0;

std::cout << std::endl << "Creating speed matrix...";

for (long i = 1; i < rows; ++i) 
{

    deltaT = rawData[i][0] - rawData[i-1][0];
    carDeltaV = rawData[i-1][3] * deltaT;
    carV[i] = carDeltaV + carV[i-1];
}

std::cout << "Done." << std::endl;

//write data to csv file
std::cout << std::endl << "Writing data to file...";

std::ofstream outputData;
outputData.open("outputdata.csv");

for(long r = 0; r < rows; ++r)
{
         outputData << rawData[r][0] << "," << rawData[r][3]/.00981 << ",";
         outputData << carV[r] << std::endl;
}

outputData.close();
std::cout << "Done." << std::endl;

//delete pointers
std::cout << std::endl << "Clearing memory...";

for(long pj = 0; pj < rows; ++pj)
{
         delete [] rawData[pj];
}
delete [] rawData;
delete [] carV;

std::cout << "Done." << std::endl;

std::cin.get();
return 0;

}

注意:列的数量将始终为 5。行是我的未知数。下面是我将要导入的示例:

0.001098633,0.011430004,0.002829004,-0.004371409,0.00162947
0.001220703,0.00606778,0.001273052,0.003497127,0.002359922
0.001342773,0.003104446,-0.000848701,0.012385657,-0.008119254

还有更多内容,但这应该足以理解我想要完成的工作。我已经阅读了一些向量,但是向量的向量的概念对我来说有点混乱,我试图实现它但没有成功。另外,我读到双端队列可能是我正在寻找的?我对这些没有经验,在我看来,这对我的应用程序来说可能有点过头了,因为我只是在一个方向上附加到一组数据。

免责声明:我几乎是 C++ 的新手,所以如果有任何你认为超出我技能水平的概念,请告诉我,以便我阅读。

有什么建议吗?

编辑:根据要求,这就是我尝试使用矢量执行此操作的方式。

std::vector<double> rawDataRow;
std::vector< std::vector<double> > rawDataMatrix;

//import raw data loop
std::ifstream rawInput("test.csv");

for(int i = 1; i > 0; ) {
          rawInput.getline(buff, 200);
          std::stringstream ss(buff);

          for(int c = 0; c < cols; ++c) {
                  ss.getline(buff, 40, ',');
                  value = atof(buff);
                  rawDataRow.push_back(value);

                  std::cout << rawDataRow[0] << std::endl;
          }
          timeDiff = timeAfter - timeBefore;
          timeBefore = timeAfter;
          timeAfter = rawDataRow[0];

          rawDataMatrix.push_back(rawDataRow);
}

其中“i”将在 eof 处设置为 0。

【问题讨论】:

  • 使用 std::vector&lt;T&gt; 和合适的类型 T 来保存元素并使用 vector.push_back(value) 附加每条记录。班级将根据需要照顾成长。
  • 你能用向量展示你的代码吗?
  • 你只使用第一列和第四列吗?如果是这样,您可以通过不将字符串转换为每行其他三个值的两倍来节省工作量。
  • @DietmarKühl 这就是我试图实现的,但没能做到。这将如何在 2D 中处理?
  • 实际上deque 听起来很适合这个应用程序,正是因为你只是在末尾追加。

标签: c++ arrays vector heap-memory deque


【解决方案1】:

总结讨论中出现的问题:

您不能拥有数组向量,请参见:Correct way to work with vector of arrays 您可以拥有指向数组的指针向量。但是,在这一点上,我不会搞乱所有的内存处理。

最好的办法是使用向量来保存代码,但应将 rawDataRow 的定义放在循环中,以便在每次迭代时清除其内容。

std::vector< std::vector<double> > rawDataMatrix;

//import raw data loop
std::ifstream rawInput("test.csv");

for(int i = 1; i > 0; ) {
      std::vector<double> rawDataRow;
      rawInput.getline(buff, 200);
      std::stringstream ss(buff);

      // do the rest
}

【讨论】:

    【解决方案2】:

    看来你的生活太艰难了。然而,关键的实现是,您总是需要检查输入以某种形式使用它之前。一旦你这样做了,事情就很容易就位了。

    为了方便处理一行的输入,我首先要定义这个简单的操纵器,它会忽略逗号:

    std::istream& comma(std::istream& in) {
        if ((in >> std::ws).peek() == ',') {
            in.ignore(); // the happy case: just skip over the comma
        }
        else if (!in.peek() == std::char_traits<char>::eof()) {
            in.setstate(std::ios_base::failbit); // unhappy: not the end and not a comma
        }
        return in;
    }
    

    有了这个,就可以很容易地读取行并将它们分成单元格:

    std::vector<std::vector<double>> result;
    for (std::string line; std::getline(in, line); ) {
        std::istringstream lin(line);
        std::vector<double> row;
        for (double d; d >> lin >> comma; ) {
            row.push_back(d);
        }
        if (!lin.eof()) {
            in.setstate(std::ios_base::failbit);
        }
        std::result.push_back(row);
    }
    if (!in.eof()) {
        std::cout << "there was an input error\n";
    }
    else {
        // result contains the result of reading...
    }
    

    我还没有测试过代码,我猜有些地方有错别字,但一般的方法应该可以工作......

    【讨论】:

    • 我确实让问题变得比它需要的更困难。在您和其他 cmets 的帮助下,我能够学到更多知识并(我希望)成为一名更好的程序员。
    【解决方案3】:

    首先,你应该把你的程序分成三个部分:

    1. 从输入文件中读取数据
    2. 处理数据
    3. 将数据写入输出文件

    你的主程序基本上应该是这样的:

    int main() {
      vector<InputRecord> data = read_from_csv("filedata.csv");
      vector<double> speeds = compute_speeds(data);
      write_to_csv("result.csv", data, speeds);
      return 0;
    }
    

    现在您需要定义InputRecord 是什么。你说这是一个包含 5 个双精度数的数组,但这不是最好的描述。它应该更像这样:

    struct InputRecord {
      double timestamp;
      double field2;
      double field3;
      double location;
      double field5;
    };
    

    使用这种数据结构,你可以写data[0].timestamp而不是data[0][0],这意味着你不再需要cmets了。

    这是我为此任务编写的完整代码。它和你做的事情类似,应该作为一个很好的起点。请注意,此代码根本不进行显式内存管理。

    #include <cstdio>
    #include <cstdlib>
    #include <fstream>
    #include <iostream>
    #include <string>
    #include <vector>
    
    using std::string;
    using std::vector;
    
    struct InputRecord {
      double timestamp;
      double field2;
      double field3;
      double location;
      double field5;
    };
    
    vector<InputRecord> read_from_csv(const char *filename) {
      std::ifstream in(filename);
      vector<InputRecord> data;
    
      if (!in.is_open()) {
        throw std::ios_base::failure(string()
            + "cannot open input file \"" + filename + "\".");
      }
    
      string line;
      while (std::getline(in, line)) {
        InputRecord rec;
        char end_of_line;
        if (std::sscanf(line.c_str(), "%lf,%lf,%lf,%lf,%lf%c",
            &rec.timestamp, &rec.field2, &rec.field3,
            &rec.location, &rec.field5, &end_of_line) != 5) {
          throw std::ios_base::failure(string()
              + "input file \"" + filename + "\" "
              + "contains invalid data: \"" + line + "\"");
        }
        data.push_back(rec);
      }
      if (in.bad()) {
        throw std::ios_base::failure(string() + "error while reading data");
      }
      return data;
    }
    
    vector<double> calculate_speeds(const vector<InputRecord> &data) {
      vector<double> speeds;
    
      speeds.push_back(0.0);
      for (std::size_t i = 1; i < data.size(); i++) {
        double delta_t = data[i].timestamp - data[i - 1].timestamp;
        double delta_s = data[i].location - data[i - 1].location;
        speeds.push_back(delta_s / delta_t);
      }
      return speeds;
    }
    
    void write_to_csv(const char *filename, const vector<InputRecord> &data,
        const vector<double> &speeds) {
      std::ofstream out(filename);
    
      if (!out.is_open()) {
        throw std::ios_base::failure(string()
            + "cannot open output file \"" + filename + "\".");
      }
      for (std::size_t i = 0; i < data.size(); i++) {
        out << data[i].timestamp << "," << speeds[i] << "\n";
      }
      if (out.bad()) {
        throw std::ios_base::failure(string() + "error while writing data");
      }
    }
    
    int main() {
      vector<InputRecord> data = read_from_csv("in.csv");
      vector<double> speeds = calculate_speeds(data);
      write_to_csv("out.csv", data, speeds);
      return 0;
    }
    

    【讨论】:

    • 感谢您的详细回复。我今天会试试这个。
    • Roland,我运行了您提供的代码(根据我的需要进行更改),它的工作原理就像一个魅力。我真的很感谢你的帮助,能学到这样的新东西真是太好了。
    猜你喜欢
    • 1970-01-01
    • 2013-03-01
    • 1970-01-01
    • 2010-09-30
    • 2010-11-05
    • 1970-01-01
    • 2010-11-25
    • 1970-01-01
    • 2011-01-18
    相关资源
    最近更新 更多