I have a set of code that will read in ints from a text file and put it in some matrix object.
fstream& operator>>(fstream& file, DynamicMatrix& matrixIn) {
string rowAndColLine;
string dataline;
char atofIsDumb[1] = { 0 };
getline(file, rowAndColLine);
atofIsDumb[0] = rowAndColLine[0]; //Rows
matrixIn.rows = atoi(atofIsDumb);
atofIsDumb[0] = rowAndColLine[2]; //Cols
matrixIn.cols = atoi(atofIsDumb);
matrixIn.matrix = new double* [matrixIn.rows];
for (int i = 0; i < matrixIn.rows; i++)
{
matrixIn.matrix[i] = new double[matrixIn.cols];
for (int j = 0; j < matrixIn.cols; j++)
{
atofIsDumb[0] = dataline[j];
matrixIn.matrix[i][j] = atof(atofIsDumb);
}
}
return file;
}
I want to modify this function so when I call the operator it will read in a double properly. The way I am reading ints is assuming that every increment of dataline is a new int. However that wont work for doubles. How can I fix this function so it will treat floating point numbers in a text file correctly?
Example I/O
4 2
1.35477 8.35009
9.68868 2.21034
3.08167 5.47221
1.88382 9.92881
2 3
9.96461 9.67695 7.25839
9.8111 1.09862 7.98106
The first line is always row col size, and then the next set of ints is for the next matrix. For readin multiple matrices I lock the file for reading and share the reading among two threads.
The usage of "atoX" is not the way to go. Here you can and should use classical formatted input functions using operator >>
.
Then everything can be fairly easy.
Below you can see one of many many potential implementations. This could be used as a basic framework to build a class with more functionalities.
We will provide functions to allocate a 2d array and to free the memory later. ALso the inserter and extraction functions >>
and <<
will be overloaded. And finally we add the rule of 5 special functions. With that, we can store all read data blocks in a std::vector
.
Example:
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
struct DynamicMatrix {
// Basic data of a matrix
double** matrix{};
size_t numberOfRows{};
size_t numberOfColumns{};
// We do not want to create memory holes
void clear() {
for (size_t row{}; row < numberOfRows; ++row)
delete[] matrix[row];
delete[] matrix;
numberOfRows = {}; numberOfColumns = {};
}
// Simple extraction operator. Will read only one block of doubles from the stream
friend std::istream& operator >> (std::istream& is, DynamicMatrix& dm) {
// Free old memory and get number of rows and columns for this block
dm.clear();
is >> dm.numberOfRows >> dm.numberOfColumns;
// Get memory for row pointers
dm.matrix = new double* [dm.numberOfRows]{};
// And now get the memory for the columns for all the rows
for (size_t row{}; row < dm.numberOfRows; ++row) {
dm.matrix[row] = new double[dm.numberOfColumns]{};
// And now read the column values
for (size_t col{}; col < dm.numberOfColumns; ++col)
is >> dm.matrix[row][col];
}
return is;
}
// Simple output
friend std::ostream& operator << (std::ostream& os, const DynamicMatrix& dm) {
for (size_t row{}; row < dm.numberOfRows; ++row) {
for (size_t col{}; col < dm.numberOfColumns; ++col)
os << dm.matrix[row][col] << '\t';
os << '\n';
}
return os;
}
// Rule of 5
// Default constructor (Elements are already initialized)
DynamicMatrix() {}
// Copy constructor
DynamicMatrix(const DynamicMatrix& dm) {
// Copy the size
numberOfRows = dm.numberOfRows;
numberOfColumns = dm.numberOfColumns;
// Get memory for row pointers
matrix = new double* [numberOfRows]{};
// And now get the memory for the columns for all the rows
for (size_t row{}; row < numberOfRows; ++row) {
matrix[row] = new double[numberOfColumns]{};
// And now copy the data
for (size_t col{}; col < numberOfColumns; ++col)
matrix[row][col] = dm.matrix[row][col];
}
}
// Copy assignment
DynamicMatrix& operator = (const DynamicMatrix& dm) {
if (this != &dm) { // not a self-assignment
// Get rid of old values
clear();
// Copy the size
numberOfRows = dm.numberOfRows;
numberOfColumns = dm.numberOfColumns;
// Get memory for row pointers
matrix = new double* [numberOfRows] {};
// And now get the memory for the columns for all the rows
for (size_t row{}; row < numberOfRows; ++row) {
matrix[row] = new double[numberOfColumns] {};
// And now copy the data
for (size_t col{}; col < numberOfColumns; ++col)
matrix[row][col] = dm.matrix[row][col];
}
}
return *this;
}
// Move constructor
DynamicMatrix(DynamicMatrix&& dm) noexcept {
// Copy size and data
numberOfRows = dm.numberOfRows;
numberOfColumns = dm.numberOfColumns;
matrix = dm.matrix;
// Clear other matrix
dm.matrix = {};
dm.numberOfRows = {};
dm.numberOfColumns = {};
}
// Move assignment
DynamicMatrix& operator = (DynamicMatrix&& dm) noexcept{
if (this != &dm) { // not a self-assignment
clear();
// Copy size and data
numberOfRows = dm.numberOfRows;
numberOfColumns = dm.numberOfColumns;
matrix = dm.matrix;
// Clear other matrix
dm.matrix = {};
dm.numberOfRows = {};
dm.numberOfColumns = {};
}
return *this;
}
// Delete memory when going out of existence
virtual ~DynamicMatrix() {
clear();
}
};
// Testdata
std::istringstream iss{ R"(4 2
1.35477 8.35009
9.68868 2.21034
3.08167 5.47221
1.88382 9.92881
2 3
9.96461 9.67695 7.25839
9.8111 1.09862 7.98106
)" };
// Test function
int main() {
// Here we will store all dynamic matrices
std::vector<DynamicMatrix> blocks{};
// One temporary matrix
DynamicMatrix dmTemp{};
// Read all block from stream
while (iss >> dmTemp) {
blocks.push_back(std::move(dmTemp));
}
// Debug output
for (const DynamicMatrix& block : blocks) {
std::cout << "\n\n" << block;
}
}
If you want to dive in deeper, I show you also a full implementation of a 1d DynamicArray, from which you can of course also build a 2d Matrix by defining an array of arrays.
#include <iostream>
#include <sstream>
#include <initializer_list>
// -----------------------------------------------------------------------------------------------
// Definition of simple dynamic array class
template <typename T>
class DynamicArray {
// The Dynamic Array has an initial capacity.
// If more elements will be added, there will be a reallocation with double capacity
static constexpr unsigned int InitialCapacity{ 8 };
// Internal data ------------------------------------------------------------------------------
T* data{}; // Dynamic Storage for Data
unsigned int numberOfElements{}; // Number of elements currently in the container
unsigned int capacity{ InitialCapacity }; // Current maximum capacity of the container
public:
// Construction and Destruction ---------------------------------------------------------------
DynamicArray(); // Default constructor. Allocate new memory
DynamicArray(const unsigned int size); // Constructor for a given size. Allocate new memory
DynamicArray(const DynamicArray& other); // Copy constructor. Make a deep copy
DynamicArray(DynamicArray&& other); // Move constructor
// Special constructors
template <class Iterator> DynamicArray(Iterator begin, Iterator end); // Initialize from range
template <int N> DynamicArray(const T(&other)[N]); // Initialize from C_Sytle array,e.g. a string literal
template <int N> DynamicArray(T(&other)[N]);
DynamicArray(const std::initializer_list<T>& list); // Take data from initializer list
~DynamicArray(); // Destructor: Release previously allocated memory
// Housekeeping ---------------------------------------------------------------
bool empty() const; // Do we have elements in the container? Do not mix up with capacity
void clear(); // Clear will not delete anything. Just set element count to 0
unsigned int size() const; // How many elements are in the container
// Main working functions
void push_back(const T& d); // Add a new element at the end
// Operators for class------------------------ ---------------------------------------------------------------
T operator[] (const unsigned int i) const; // Index operator, get data at given index. No boundary check
T& operator[] (const unsigned int i); // Index operator, get data at given index. No boundary check
DynamicArray& operator=(const DynamicArray& other); // Assignment
DynamicArray& operator=(DynamicArray&& other); // Move Assignment
// Add iterator properties to class ---------------------------------------------------------------
class iterator { // Local class for iterator
T* iter{}; // This will be the iterator
T* begin{}; // For boundary check
T* end{}; // For boundary check
public: // Define alias names necessary for the iterator functionality
using iterator_category = std::random_access_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = T;
using pointer = T*;
using reference = T&;
// Constructor
iterator(T* const i, T* const b, T* const e);
// Dereferencing
reference operator *() const;
pointer operator ->() const;
// Aithmetic operations
iterator& operator ++();
iterator& operator --();
iterator operator ++(int);
iterator operator --(int);
iterator operator +(const difference_type& n) const;
iterator& operator +=(const difference_type& n);
iterator operator -(const difference_type& n) const;
iterator& operator -=(const difference_type& n);
// Comparison
bool operator ==(const iterator& other) const { return iter == other.iter; };
bool operator !=(const iterator& other) const { return iter != other.iter; };
bool operator < (const iterator& other) const { return other.iter - iter < 0; };
bool operator <= (const iterator& other) const { return other.iter - iter <= 0; };
bool operator > (const iterator& other) const { return other.iter - iter > 0; };
bool operator >= (const iterator& other) const { return other.iter - iter >= 0; };
// Reference and difference
reference operator[] (const difference_type& n);
difference_type operator-(const iterator& other) const;
};
// Begin and end function to initialize an iterator
iterator begin() const;
iterator end() const;
// Working functions dealing with iterators. More may be added
iterator erase(iterator pos);
DynamicArray<T> split();
};
template <typename T>
DynamicArray<T> DynamicArray<T>::split() {
DynamicArray<T> result;
if (numberOfElements > 1) {
const size_t offset = numberOfElements / 2;
result.numberOfElements = numberOfElements - offset;
result.data = data + offset;
result.capacity = result.numberOfElements << 1;
numberOfElements = offset;
}
return result;
}
// Default constructor. Allocate new memory
template <typename T>
inline DynamicArray<T>::DynamicArray() {
data = new T[capacity];
}
// Constructor for certain size. Allocate new memory
template <typename T>
inline DynamicArray<T>::DynamicArray(const unsigned int size) : data(new T[size]), numberOfElements(0), capacity(size) {
}
// Copy constructor
template <typename T>
DynamicArray<T>::DynamicArray(const DynamicArray& other) { // Copy constructor. Make a deep copy
capacity = numberOfElements = other.numberOfElements;
data = new T[capacity]; // Get memory, same size as other container
for (size_t k = 0; k < other.numberOfElements; ++k)
data[k] = other.data[k]; // Copy data
}
// Move constructor
template <typename T>
DynamicArray<T>::DynamicArray(DynamicArray&& other) {
data = other.data;
numberOfElements = other.numberOfElements;
capacity = other.capacity;
other.capacity = InitialCapacity;
other.numberOfElements = 0;
other.data = new T[capacity];;
}
// Range constructor
template <typename T>
template <class Iterator>
DynamicArray<T>::DynamicArray(Iterator begin, Iterator end) {
data = new T[capacity];
for (Iterator i = begin; i != end; ++i)
push_back(*i);
}
// Construct from a const C-Style Array, like for example "Hello"
template <typename T>
template <int N>
DynamicArray<T>::DynamicArray(const T(&other)[N]) {
capacity = numberOfElements = N;
data = new T[capacity]; // Get memory, same size as other container
for (size_t k = 0; k < N; ++k)
data[k] = other[k]; // Copy data
}
// Construct from a C-Style Array
template <typename T>
template <int N>
DynamicArray<T>::DynamicArray(T(&other)[N]) {
capacity = numberOfElements = N;
data = new T[capacity]; // Get memory, same size as other container
for (size_t k = 0; k < N; ++k)
data[k] = other[k]; // Copy data
}
// Construct from an initializer list
template <typename T>
DynamicArray<T>::DynamicArray(const std::initializer_list<T>& list) {
data = new T[capacity];
for (const T& t : list) push_back(t);
}
// Destructor will release the dynamic allocated memory
template <typename T>
inline DynamicArray<T>::~DynamicArray() {
delete[] data;
} // Destructor: Release previously allocated memory
// Some houskeeping functions
template <typename T>
inline bool DynamicArray<T>::empty() const {
return numberOfElements == 0;
}
template <typename T>
inline void DynamicArray<T>::clear() {
numberOfElements = 0;
}; // Clear will not delete anything. Just set element count to 0
template <typename T>
inline unsigned int DynamicArray<T>::size() const {
return numberOfElements;
} // How many elements are in the container
// Main workhorse for a dynamic array.
// Store element, and alwaysprovide enough memory
template <typename T>
void DynamicArray<T>::push_back(const T& d) { // Add a new element at the end
if (numberOfElements >= capacity) { // Check, if capacity of this dynamic array is big enough
capacity *= 2; // Obviously not, we will double the capacity
T* temp = new T[capacity]; // Allocate new and more memory
for (unsigned int k = 0; k < numberOfElements; ++k)
temp[k] = data[k]; // Copy data from old memory to new memory
delete[] data; // Release old memory
data = temp; // And assign newly allocated memory to old pointer
}
data[numberOfElements++] = d; // And finally, store the given data at the end of the container
}
// Operators for class ------------------------ ---------------------------------------------------------------
template <typename T>
inline typename T DynamicArray<T>::operator[] (const unsigned int i) const {
return data[i];
} // Index operator, get data at given index. No boundary check
template <typename T>
inline typename T& DynamicArray<T>::operator[] (const unsigned int i) {
return data[i];
} // Index operator, get data at given index. No boundary check
// Assignement operator. Make a deep copy
template <typename T>
DynamicArray<T>& DynamicArray<T>::operator=(const DynamicArray& other) {
if (this != &other) { // Prevent self-assignment
delete[] data; // Release any previosly existing memory
capacity = numberOfElements = other.numberOfElements;// Take over capacity and number of elements from other container
data = new T[capacity]; // Get new memory, depending on size of other
for (unsigned int k = 0; k < numberOfElements; ++k) // Copy other data
data[k] = other.data[k];
}
return *this;
}
template <typename T>
DynamicArray<T>& DynamicArray<T>::operator=(DynamicArray&& other) { // Move Assignment
if (this != &other) { // Prevent self-assignment
data = other.data;
numberOfElements = other.numberOfElements;
capacity = other.capacity;
other.capacity = InitialCapacity;
other.numberOfElements = 0;
other.data = new T[capacity];;
}
return *this;
}
// Implementation of iterator functions ---------------------------------------------------------------------
// COnstruction
template <typename T>
inline DynamicArray<T>::iterator::iterator(T* const i, T* const b, T* const e) : iter(i), begin(b), end(e) {
}; // Constructor for the iterator
// Dereferencing
template <typename T>
inline typename DynamicArray<T>::iterator::reference DynamicArray<T>::iterator::operator *() const {
return *iter;
}
template <typename T>
inline typename DynamicArray<T>::iterator::pointer DynamicArray<T>::iterator::operator ->() const {
return iter;
}
// Arithmetic operations
template <typename T>
inline typename DynamicArray<T>::iterator& DynamicArray<T>::iterator::operator ++() {
if (iter < end)
++iter;
return *this;
}
template <typename T>
inline typename DynamicArray<T>::iterator& DynamicArray<T>::iterator::operator --() {
if (iter > begin)
--iter;
return *this;
}
template <typename T>
typename DynamicArray<T>::iterator DynamicArray<T>::iterator::operator ++(int) {
DynamicArray<T>::iterator tmp = *this;
if (this->iter < end)
++(*this);
return tmp;
}
template <typename T>
typename DynamicArray<T>::iterator DynamicArray<T>::iterator::operator --(int) {
DynamicArray<T>::iterator tmp = *this;
if (this->iter > begin)
--(*this);
return tmp;
}
template <typename T>
typename DynamicArray<T>::iterator DynamicArray<T>::iterator::operator +(const DynamicArray<T>::iterator::difference_type& n) const {
DynamicArray<T>::iterator tmp = *this;
DynamicArray<T>::iterator::difference_type k{ n };
if (k > 0)
while (k--)
++tmp;
else
while (k++)
--tmp;
return tmp;
}
template <typename T>
typename DynamicArray<T>::iterator& DynamicArray<T>::iterator::operator +=(const DynamicArray<T>::iterator::difference_type& n) {
DynamicArray<T>::iterator::difference_type k{ n };
if (k > 0)
while (k--)
++* this;
else
while (k++)
--* this;
return *this;
}
template <typename T>
typename DynamicArray<T>::iterator DynamicArray<T>::iterator::operator- (const DynamicArray<T>::iterator::difference_type& n) const {
DynamicArray<T>::iterator tmp = *this;
DynamicArray<T>::iterator::difference_type k{ n };
if (k > 0)
while (k--)
--tmp;
else
while (k++)
++tmp;
return tmp;
}
template <typename T>
typename DynamicArray<T>::iterator& DynamicArray<T>::iterator::operator -=(const typename DynamicArray<T>::iterator::difference_type& n) {
DynamicArray<T>::iterator::difference_type k{ n };
if (k > 0)
while (k--)
--* this;
else
while (k++)
++* this;
return *this;
}
// Index operator for iterator
template <typename T>
inline typename DynamicArray<T>::iterator::reference DynamicArray<T>::iterator::operator[] (const typename DynamicArray<T>::iterator::difference_type& n) {
return *(iter + n);
};
template <typename T>
typename DynamicArray<T>::iterator::reference DynamicArray<T>::iterator::operator-(const iterator& other) const {
difference_type result{};
int indexThis{ -1 }, indexOther{ -1 }, index{};
do {
;
if (nptr == iter)
indexThis = index;
if (nptr == other.iter)
indexOther = index;
++index;
} while (nptr != head);
if (indexThis >= 0 and indexOther >= 0)
result = indexThis - indexOther;
return result;
}
// Delta
template <typename T>
inline typename DynamicArray<T>::iterator::difference_type DynamicArray<T>::iterator::operator-(const typename DynamicArray<T>::iterator& other) const {
return iter - other.iter;
}
// ------------------------------------------------------------------------
// Get iterators for dynamic array
template <typename T>
inline typename DynamicArray<T>::iterator DynamicArray<T>::begin() const {
return iterator(data, data, data + numberOfElements);
}
template <typename T>
inline typename DynamicArray<T>::iterator DynamicArray<T>::end() const {
return iterator(data + numberOfElements, data, data + numberOfElements);
}
// ------------------------------------------------------------------------
// Any other functions for dynamic array
template <typename T>
typename DynamicArray<T>::iterator DynamicArray<T>::erase(typename DynamicArray<T>::iterator pos) {
iterator result{ pos };
if (pos != end()) {
while (pos != end()) {
*pos = *(pos + 1);
++pos;
}
++result;
--numberOfElements;
}
return result;
}
int main() {
DynamicArray<int> da1{ 1,2,3,4,5,6 };
DynamicArray<int> da2 = da1.split();
for (const int i : da1)
std::cout << i << ' ';
std::cout << "\n\n";
for (const int k : da1)
std::cout << k << ' ';
}