Search code examples
c++fstreamfriend

How to read in doubles from a text file in a more efficient way?


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.


Solution

  • 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 << ' ';
    }