I will present one possible solution (out of 42 million options :-)
The idea is to use an Object Oriented approach. So, a class with data and methods.
The data is the required vector of vector of float. And the methods are extraction and addition. For debug purposes, there is also an overwrite of the inserter operator.
Please note: The tricky part is parsing the input string. Especially with white spaces that could be everywhere. For ease of use, I defined a regex for a float value. The regex_token_iterator will extract the sub matches and copy those into a column. And this until the next ";". Looks complex, but is easy to understand in the end.
I put many comments in the code, which makes it a little bit lengthy. Anyway, it will help to understand better.
The main function will look very simple then . . .
EDIT:
Based on the recomendation of David C. Rankin, I updated the source code:
- Modify variable names to make them more readable
- Changed base data type from float to double
- Add more white spaces and line breaks for a better text structure
- Put in more comments
- New size check for addition operator
#include <iostream>
#include <sstream>
#include <string>
#include <algorithm>
#include <regex>
// Our test data (raw string) . We could of course also read from a file or from std::cin or any istream
std::istringstream testData(
R"#( [ 1 2 3 ; 4 5 6 7 ]
[ 7 8 9; 10 11 12]
)#");
struct Matrix
{
// The data of our matrix
std::vector<std::vector<double>> data{};
// Overloading the extractor operator >> for our Matrix class
friend std::istream& operator >> (std::istream& is, Matrix& m) {
// Read a complete line
std::string line;
getline(is, line);
// Define a regex that matches any double number
std::regex re("([-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?)");
// Set iterators.
// "Start" will be set to begin() or one past the end of the next semicolon
std::string::iterator start = line.begin();
// Position of semincolon or end()
std::string::iterator posSemicolon{};
// Read all rows and columns
do {
// Set the end iterator for this loop run to the position of the semicolon (or end() )
posSemicolon = std::find(start, line.end(), ';');
// Add a new row
m.data.emplace_back(std::vector<double>());
// Read a complete line with doubles and put that as column values in the current row
std::transform(
std::sregex_token_iterator(start, posSemicolon, re, 1), // Search for text that matches the regex
std::sregex_token_iterator(), // Search until the end (posSemicolon)
std::back_inserter(*(std::prev(m.data.end()))), // Put this data into the columns of the current row
// Convert the string matched by the regex, into a double
[](const std::string& stringAsDouble) -> double { return stod(stringAsDouble); }
);
// In case there is more input,
if (posSemicolon != line.end()) {
// then continue tokenizing in the next loop run past the semicolon
start = posSemicolon + 1;
}
} while (posSemicolon != line.end());
return is;
}
// For debug output purposes. Copy the data of the matrix to std::cout
friend std::ostream& operator << (std::ostream & os, const Matrix & m) {
std::for_each(
m.data.begin(), // Iterate over all rows
m.data.end(),
[&os](const std::vector<double> & row) { // Do for all rows
std::copy( // Copy data to ostream (print)
row.begin(), // Iterate ove all columns for this row
row.end(),
std::ostream_iterator<double>(os, " ")); // Print
std::cout << '\n'; // Line break after printing a row
}
);
return os;
}
// Overload of the addition operator
friend Matrix operator +(Matrix & m1, Matrix & m2) {
// Define an empty matrix
Matrix result{};
// To be on the safe side, we will resize the destination matrix size
// to maximum values of both matrices
// Get the max number of rows ofrom both m1 and m2
const size_t maxRowSize = std::max(m1.data.size(), m2.data.size());
// Set number of rows in resulting Matrix
result.data.resize(maxRowSize);
// Get the maximum number of columns in any of the 2 metrices m1 or m2
const size_t maxColumnSize = std::max(
std::max_element(
m1.data.begin(), // Iterate over all rows of Matrix m1
m1.data.end(),
[](const std::vector<double> & dv1_1, const std::vector<double> & dv1_2) {
return dv1_1.size() < dv1_2.size();
}
)->size(),
std::max_element(
m2.data.begin(), // Iterate over all rows of Matrix m1
m2.data.end(),
[](const std::vector<double> & dv2_1, const std::vector<double> & dv2_2) {
return dv2_1.size() < dv2_2.size();
}
)->size()
);
// Iterate over all matrix elements
// For all rows
for (size_t row = 0; row < maxRowSize; ++row) {
// Resize the the number of columns in the target matrix
result.data[row].resize(maxColumnSize);
// Now iterate over all columns in that row
for (size_t col = 0; col < maxColumnSize; ++col) {
// And add the values. First check for valid row and column indices
double m1Value = ((row < m1.data.size()) && (col < m1.data[row].size())) ? m1.data[row][col] : 0.0;
double m2Value = ((row < m2.data.size()) && (col < m2.data[row].size())) ? m2.data[row][col] : 0.0;
result.data[row][col] = m1Value + m2Value;
}
}
return result;
}
};
int main()
{
// Define some matrices
Matrix m1, m2, m3;
// Read the test data. You can also read from std::cin or from a file
testData >> m1 >> m2;
// Add the Matrices
m3 = m1 + m2;
// Show result
std::cout << "Matrix 1:\n" << m1 << "\nMatrix 2:\n" << m2 << "\nMatrix3 = Matrix1 + Matrix2:\n" << m3;
return 0;
}