如何将逗号分隔的数字矩阵放入 C++ 中的动态分配数组中?

How do I fit in a comma separated number matrix into a dynamically allocated array in C++?

我有一个文件存储未知形状的数字矩阵,格式如下

-1,4,12,5.7
2.1,3,-10,3.3
7.1,1.11,12,10

我尝试将矩阵存储在动态分配的数组中,因为我无法对行数和列数进行硬编码。对于这部分,我使用了指针到指针,演示如下,

#include <iostream>
using namespace std;

int main()
{
    // Rather than user input, I need to change this part to deciding the shape of the matrix myself
    int row, col;
    cout << "Enter row number and column number, separated with a space:\n";
    cin >> row >> col;
    int** p_p_grid = new int* [row];
    for(int i = 0; i < row; i++)
    {
        p_p_grid[i] = new int[col];
    }

    // Fill in the entries
    for(int i = 0; i < row; i++)
    {
        for(int j = 0; j < col; j++)
        {
            // (i + 1) * (j + 1) needs to be replaced by true entries in the matrix
            p_p_grid[i][j] = (i + 1) * (j + 1);
        }
    }
    return 0;
}

但是在一个一个分配数字之前决定逗号分隔数字块形状的有效方法是什么?还有如何在 C++ 中导入 CSV 结构矩阵? (由于某些原因,我不想使用向量类型,所以请关注数组)

what is an efficient way to decide the shape of a comma separated number block before assigning the number one by one?

假设您正在从文件流中读取,最简单的方法是读取文件两次:一次用于计算行数和逗号,一次用于进行实际输入。

这里是一个如何检测矩阵结束的例子,当新行的元素数量与矩阵的格式不匹配时停止:

int nrows=1, ncols=0;
string line; 
while (getline(ifs, line)) {
    int n=1; 
    for (auto x: line)   // count commas in the line 
        if (x==',') 
            n++; 
    if (!ncols) 
        ncols = n;        // first line sets th enumber of columns
    else if (n == ncols)  // subsequent lines increase the row count
        nrows++; 
    else break;          // unless the format does'n match anymore
}
ifs.clear();   // remove eof 
ifs.seekg (0, ifs.beg);  // rewind

Online demo

导致这种方法效率低下的原因是您读取了文件两次。因此,顺便说一句,您不能使用这种方法来读取 cin:您不能倒带。

您可以通过缓存读取的行(但您需要管理一个字符串的行数组,因为不允许使用向量)或让矩阵动态增长(没有不再对应您的问题,因为这不会预先提供矩阵大小)。

how do I import a CSV-structured matrix in C++

在每一行中,只读双打,然后是一个字符(应该是 ','):

char forget_me; 
for (int i=0; i<nrows; i++) 
    for (int j=0; j<ncols; j++) { 
         cin >> p_p_grid[i][j];  
         if (j<ncols-1) 
            cin>>forget_me; 
    }

我知道您不想要基于 vector 的解决方案,但这里有一个解决方案

int main() {
    ifstream input("input.txt");
    if(!input) {
        cerr << "couldn't open file" << endl;
        exit(1);
    }

    double number;
    vector<vector<double>> matrix;
    vector<double> current_row;
    while(input >> number) { // loop once for each number in the file
        current_row.push_back(number);
        int next_char = input.get(); // should be ',' or '\n'
        if(next_char == '\n') {
            // current row is finished
            matrix.push_back(current_row);
            current_row.clear();
        }   
    }   

    // now print the matrix back out again
    for(auto const & one_row : matrix) {
        for(auto one_number : one_row) {
            cout << "\t," << one_number;
        }   
        cout << endl;
    }   
}

有点笨拙。使用 C++ iostream 或 C stdio.h,并读入整行。所以如果使用 getline / fgets,你需要一个非常大的缓冲区,比如 8k。现在对于第一行,解析为字段。第一次尝试时,只需计算逗号,但实际规则比这更复杂。

逐行检查,提取数据。由于您对行数没有发言权,因此除了为每一行动态增加缓冲区之外别无选择。 STL 向量使这对您来说很容易 - 只需将其推回,缓冲区就会增长。但是,如果您愿意,可以使用此结构

int **p_p_grid = 0;
int Nrows = 0;
int Ncolumns = 0;

/* ( for first line, fill the number of columns) */
/* for each line */
p_p_grid = realloc((Nrows + 1) * sizeof(int *));
if(!p_p_grid) 
  memory_failure();
p_p_grid[Nrows] = malloc(Ncolums * sizeof(int));
if(!p_p_grid[Nrows])
  memory_failure();
for(i=0;i<Ncolumns;i++)
   p_p_grid[Nrows][i] = /* parse logic here */ 
Nrows++;

如前所述,我会改用 std::vector。考虑到每一行都有固定数量的元素,我也会使用 std::array。也许做这样的事情:

#include <vector>   // For std::vector
#include <array>    // For std::array
#include <string>   // For std::string and std::getline
#include <fstream>  // For std::ifstream
#include <sstream>  // For std::isstream

int main()
{
    std::vector<std::array<double, 4>> grid;

    std::ifstream input{"input.txt"};

    std::string line;

    // Outer loop reads the rows
    while(std::getline(input, line))
    {

        int i = 0;
        std::istringstream iss{line};

        std::array<double, 4> values;
        double value;

        // Inner loop extracts the values on each row
        while (iss >> value)
        {
            values[i] = value;

            // Skip over the comma
            iss.ignore(std::numeric_limits<std::streamsize>::max(), ',');
        }

        grid.push_back(values);
    }

    // Now print the values
    int row_number = 1;
    for (auto const& row : grid)
    {
        std::cout << "Row " << row_number++ << ": ";

        for (auto const value : row)
        {
            std::cout << value << ' ';
        }

        std::cout << '\n';
    }
}

请注意,我实际上并未测试上面的代码。不过它编译得很干净。