1

想知道是否有人可以帮我构建一个程序,该程序可以从 csv 文件中读取大小未知的浮点大数据块。我已经在 MATLAB 中编写了这个,但想编译和分发它,所以转向 c++。

我只是在学习并尝试阅读本文开始

7,5,1989
2,4,2312

从文本文件。

到目前为止的代码。

// Read in CSV
//
// Alex Byasse

#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <sstream>
#include <stdlib.h>

int main() {

    unsigned int number_of_lines = 0;
    FILE *infile = fopen("textread.csv", "r");
    int ch;
    int c = 0;
    bool tmp = true;
    while (EOF != (ch=getc(infile))){
      if(',' == ch){
    ++c;
      }
      if ('\n' == ch){
    if (tmp){
      int X = c;
      tmp = false;
    }
            ++number_of_lines;
    }
    }
    fclose(infile);

  std::ifstream file( "textread.csv" );

  if(!file){
    std:cerr << "Failed to open File\n";
    return 1;
  }

  const int ROWS = X;
  const int COLS = number_of_lines;
  const int BUFFSIZE = 100;
  int array[ROWS][COLS];
  char buff[BUFFSIZE];
  std::string line; 
  int col = 0;
  int row = 0;
  while( std::getline( file, line ) )
  {
    std::istringstream iss( line );
    std::string result;
    while( std::getline( iss, result, ',' ) )
      {
        array[row][col] = atoi( result.c_str() );
        std::cout << result << std::endl;
        std::cout << "column " << col << std::endl;
        std::cout << "row " << row << std::endl;
        col = col+1;
    if (col == COLS){
    std:cerr << "Went over number of columns " << COLS;
    }
      }
    row = row+1;
    if (row == ROWS){
      std::cerr << "Went over length of ROWS " << ROWS;
    }
    col = 0;
  }
  return 0;
}

我使用的 matlab 代码是 >>

fid = fopen(twoDM,'r');

s = textscan(fid,'%s','Delimiter','\n');
s = s{1};
s_e3t = s(strncmp('E3T',s,3));
s_e4q = s(strncmp('E4Q',s,3));
s_nd = s(strncmp('ND',s,2));

[~,cell_num_t,node1_t,node2_t,node3_t,mat] = strread([s_e3t{:}],'%s %u %u %u %u %u');
node4_t = node1_t;
e3t = [node1_t,node2_t,node3_t,node4_t];
[~,cell_num_q,node1_q,node2_q,node3_q,node_4_q,~] = strread([s_e4q{:}],'%s %u %u %u %u %u %u');
e4q = [node1_q,node2_q,node3_q,node_4_q];
[~,~,node_X,node_Y,~] = strread([s_nd{:}],'%s %u %f %f %f');

cell_id = [cell_num_t;cell_num_q];
[~,i] = sort(cell_id,1,'ascend');

cell_node = [e3t;e4q];
cell_node = cell_node(i,:);

任何帮助表示赞赏。亚历克斯

4

4 回答 4

6

显然,我只会使用 IOStreams。从 CSV 文件中读取一个或多个同构数组而不必费心任何引用是相当简单的:

#include <iostream>
#include <sstream>
#include <string>
#include <vector>

std::istream& comma(std::istream& in)
{
    if ((in >> std::ws).peek() != std::char_traits<char>::to_int_type(',')) {
        in.setstate(std::ios_base::failbit);
    }
    return in.ignore();
}

int main()
{
    std::vector<std::vector<double>> values;
    std::istringstream in;
    for (std::string line; std::getline(std::cin, line); )
    {
        in.clear();
        in.str(line);
        std::vector<double> tmp;
        for (double value; in >> value; in >> comma) {
            tmp.push_back(value);
        }
        values.push_back(tmp);
    }

    for (auto const& vec: values) {
        for (auto val: vec) {
            std::cout << val << ", ";
        }
        std::cout << "\n";
    }
}

鉴于文件的简单结构,逻辑实际上可以简化:如果自动读取分隔符,则每一行都可以视为一系列值,而不是单独读取值。由于不会自动读取逗号,因此在为内部行创建字符串流之前,逗号会被替换为空格。对应的代码变成

#include <algorithm>
#include <fstream>
#include <iostream>
#include <iterator>
#include <sstream>
#include <string>
#include <vector>

int main()
{
    std::vector<std::vector<double> > values;
    std::ifstream fin("textread.csv");
    for (std::string line; std::getline(fin, line); )
    {
        std::replace(line.begin(), line.end(), ',', ' ');
        std::istringstream in(line);
        values.push_back(
            std::vector<double>(std::istream_iterator<double>(in),
                                std::istream_iterator<double>()));
    }

    for (std::vector<std::vector<double> >::const_iterator
             it(values.begin()), end(values.end()); it != end; ++it) {
        std::copy(it->begin(), it->end(),
                  std::ostream_iterator<double>(std::cout, ", "));
        std::cout << "\n";
    }
}

这是发生的事情:

  1. 目的地values被定义为 的向量的向量double。没有任何东西可以保证不同的行大小相同,但是一旦读取文件就可以轻松检查。
  2. std::ifstream用文件定义和初始化一个。可能值得在构建后检查文件以查看它是否可以打开以供阅读(if (!fin) { std::cout << "failed to open...\n";)。
  3. 该文件一次处理一行。只需读取这些行,即可将std::getline()它们读入std::string. 当std::getline()失败时,它无法读取另一行并且转换结束。
  4. 读取后,所有逗号都将line替换为空格。
  5. 从如此修改line的字符串流被构造用于读取该行。原始代码重用了std::istringstream在循环外声明的 a ,以节省一直构建流的成本。由于行完成后流变坏,因此首先需要in.clear()在其内容设置之前对其进行编辑in.str(line)
  6. 单个值使用一个迭代,std::istream_iterator<double>它只是从构造它的流中读取一个值。给定的迭代器in是序列的开始,默认构造的迭代器是序列的结束。
  7. 迭代器产生的值序列用于立即构造std::vector<double>表示行的临时值。
  8. 临时向量被推到目标数组的末尾。

之后的一切只是使用 C++11 功能(基于范围的 for 和具有auto数学推导类型的变量)简单地打印生成的矩阵的内容。

于 2013-09-16T00:20:57.830 回答
1

正如这里建议的那样,更改 getline 转义可能会帮助您更好地阅读 csv 文件,但您需要将类型从字符串更改为 int。

为了处理任意数量的行和列,您可以使用多维向量(向量内部的向量,如此所述),然后您将每一行放在一个向量中,将所有行放在更大的向量中

于 2013-09-16T00:31:53.067 回答
0

我打算将此作为对 Dietmar Kuhl 解决方案的编辑,但由于编辑过大而被拒绝...

将 Matlab 转换为 C++ 的通常原因是性能。所以我对这两种解决方案进行了基准测试。我使用 G++ 4.7.3 为 cygwin 编译了以下选项“-Wall -Wextra -std=c++0x -O3 -fwhole-program”。我在 32 位 Intel Atom N550 上进行了测试。

作为输入,我使用了 2 个 10,000 行文件。第一个文件是每行 10 个“0.0”值,第二个文件是每行 100 个“0.0”值。

我使用 time 从命令行进行计时,并使用了 user+sys 在三个运行中的总和的平均值。

我修改了第二个程序以从std::cin第一个程序中读取。

最后,我再次运行测试std::cin.sync_with_stdio(false);

结果(以秒为单位的时间):

               sync                no sync
        10/line  100/line     10/line  100/line
prog A    1.839    16.873       0.721     6.228
prog B    1.741    16.098       0.721     5.563

显而易见的结论是版本 B 稍微快一些,但更重要的是,您应该禁用与 stdio 的同步。

于 2013-09-16T04:19:29.747 回答
0
int fclose(infile);

这条线是错误的。编译器认为您正在尝试fclose使用 a初始化变量FILE*,这是错误的。如果您只是想关闭文件,应该是这样:

fclose(infile);
于 2013-09-16T00:21:06.427 回答