0

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.

A M
  • 14,694
  • 5
  • 19
  • 44
  • To convert a digit character to the corresponding value, just subtract `'0'`. `matrixIn.rows = rowAndColLine[0] - '0';`. – Pete Becker Oct 03 '22 at 17:49
  • 1
    I'd use formatted input for this. `file >> row >> col;` and then the same for the number of doubles you need to read. I'd also likely use a `std::vector` for the matrix but perhaps you have reasons for not doing that. If you can use a one dimensional array and handle the offset calculation in `operator[]` it would be simpler, easier to clean up, and perhaps faster. – Retired Ninja Oct 03 '22 at 17:53
  • [See this answer](https://stackoverflow.com/questions/21943621/how-to-create-a-contiguous-2d-array-in-c/21944048#21944048) as to how to create contiguous storage when given a `double **`. – PaulMcKenzie Oct 03 '22 at 17:55
  • `atoi()` and `atof()` expect a **null-terminated** string as input, but `atofIsDumb` is NOT null-terminated (since it can only hold 1 `char` at a time), thus the results are *undefined behavior*. `atofIsDumb` needs to hold 2 `char`s instead, where the 1st `char` is the desired value, and the 2nd `char` is `'\0'`. – Remy Lebeau Oct 03 '22 at 22:27

1 Answers1

0

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 << ' ';
}
A M
  • 14,694
  • 5
  • 19
  • 44