Search code examples
c++dynamic-arrays

Reading integer input from file into a dynamic array and printing it


I am attempting to write a program that reads in integers from an input file and outputs a dynamic array. This array would be the size of the input. For verification, I'd also like to print the array. The output will be passed to a function to create a sorted linked list.

I tried the following, but it did not work:

istringstream input(R"inp(
23 43 12 67 
18 15 22
12 xxx 23 12 xx 34556 11 11 www
)inp");

int get, count = 0;

while (input >> get)
    count++;

cout << "Number of integers: " << count << endl;

int *array = new int [count];


for (int i = 0; i < count; i++)
{
    input >> array[i];
}

for (int i = 0; i < count; i++)
{
    cout << *(array+i) << endl;
}

delete[] array;

Here's an online example of my code.

The problem is that the output shows some weird numbers, completely unrelated to the input:

Number of integers: 8
-1217944384
-1217944384
-1
538976266
540226080
824193844

Where did I go wrong?


Solution

  • As πάντα ῥεῖ pointed out, the solutions that I did provide are not totally safe, that's why I will provide a third example, using boost::spirit.

    See the points fast fix and good solution, as well as πάντα ῥεῖ's answer to get it working without using boost.

    my personal favourite solution: note that this example does require to have read the text file into a string.

    #include <boost/spirit/include/phoenix_stl.hpp>
    #include <boost/spirit/include/qi.hpp>
    #include <boost/spirit/include/phoenix_operator.hpp>
    
    template<typename Iterator>
    bool
    parse_numbers(Iterator first, Iterator last, std::vector<int>& v)
    {
        bool r =
        boost::spirit::qi::parse(first, last,
        //  Begin grammar
            (boost::spirit::qi::int_[boost::phoenix::push_back(
                boost::phoenix::ref(v), _1)]
                % *(boost::spirit::qi::char_("a-zA-Z")
                    | boost::spirit::ascii::space)));
    
        if (first != last) // fail if we did not get a full match
           return false;
        return r;
    }
    
    const std::string s = "23 43 12 67 \n18 15 22\n12 xxx 23 12 xx 34556 11 11 www";
    std::string::const_iterator start = s.begin(), stop = s.end();
    std::vector<int> results;
    parse_numbers(start, stop, results)));
    for(int i : results)
        std::cout << value << ' '; 
    

    the result would be, as expected:

    23 43 12 67 18 15 22 12 23 12 34556 11 11
    

    The above example is partially built on the example given in the boost::spirit documentation.

    input >> get moves the current curser position, so after your while loop you have nothing left to read.

    fast fix:

    ifstream input;
    input.open("file.txt");
    
    int get, count = 0;
    
    while (input >> get)
        count++;
    input.close();
    input.open("file.txt");    
    int *array = new int [count];    
    
    for (int i = 0; i < count; i++)
    {
        input >> array[i];
    }
    for (int i = 0; i < count; i++)
    {
        cout << *(array+i) << endl;
    }
    
    input.close();
    delete[] array;
    

    To close and reopen the stream should work, but there are more efficient solutions out there...

    good solution:

    One could be to read and insert into a dynamically growing vector for example. See the documentation for further reference.

    std::vector<int> dataArray;
    while (input >> get)
    {
        dataArray.insert(dataArray.end(), get);
    }
    for(auto&& value : dataArray)
    {
        std::cout << value << std::endl; 
    }
    

    That would have multiple advantages:

    1. allocating the vector on the stack prevents you from being forced to call delete. An alternative would be a standard smart pointer.
    2. the for each loop works even without counting the elements. If you need the number of elements you could just ask the vector about his size.