themistocles
themistocles

Reputation: 25

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?

Upvotes: 2

Views: 6986

Answers (2)

πάντα ῥεῖ
πάντα ῥεῖ

Reputation: 1

Your code has several misconceptions and flaws:

(1) After applying this loop to count your inputs

 while (input >> get)
    count++;

the input stream's state is left over the result of last extraction operation (input >> get) that has failed. Thus no further input can be read, without completely resetting the stream.

(2) The second loop you're showing

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

uses the input stream in invalid state (the whole stream was already read to input.eof()), and thus reading from it results in 'weird values' (in other words: It's undefined behavior at this point).


I would write the following proven code to solve this

// Your literal input file formatting goes here
istringstream input(R"inp(
23 43 12 67 
18 15 22
12 xxx 23 12 xx 34556 11 11 www
)inp");

int current;
vector<int> allIntInputs;

while (input >> current || !input.eof()) {
    if(input.fail()) {
        input.clear();
        string crap;
        input >> crap; // read anything up to the next 
                       // whitespace delimiter (the default deleimiters)
        continue; // with the next item
    }
    // Everything's fine we'll add another number
    allIntInputs.push_back(current);
}

// Print all integer values extracted
cout << "Integer values read from input:" << endl;
for(vector<int>::iterator it = allIntInputs.begin();
    it != allIntInputs.end();
    ++it) {
    if(it != allIntInputs.begin()) {
        cout << ' ';
    }
    cout << *it;    
}
cout << endl;

Output

Integer values read from input:
23 43 12 67 18 15 22 12 23 12 34556 11 11

Upvotes: 1

Theolodis
Theolodis

Reputation: 5102

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.

Upvotes: 1

Related Questions