2

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?

7
  • 4
    "Did not work for me" is not an adequate description of your problem. Give full details on how it did not work (including where applicable observed vs. expected behaviour and/or error messages). Also, presumably you chose C++ over C because you wanted its features, so use std::vector instead of raw arrays. And you should avoid new and delete unless you have a good reason to use them. Commented Jul 8, 2014 at 18:40
  • 1
    new[] and delete[] should almost never be seen in good C++ code. Don't make your life harder, use std::vector. Commented Jul 8, 2014 at 18:44
  • 1
    [dynamic-arrays] is not a tag for dynamically allocated arrays, but dynamically resizing at run-time without explicit memory allocation. Commented Jul 8, 2014 at 18:46
  • 1
    For starters: Use std::vector<int> array; instead of int *array; and push_back(). Also see how to extract the integer values from the stream: How to test whether stringstream operator>> has parsed a bad type and skip it Commented Jul 8, 2014 at 18:48
  • 1
    @crashmstr: Er, why's that? Commented Jul 8, 2014 at 18:51

2 Answers 2

1

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.
Sign up to request clarification or add additional context in comments.

7 Comments

@πάνταῥεῖ what? sorry, I was editing my question... I did provide a fast answer and edited to add the good answer...
I will try to use vectors then, I am a beginner so I was trying to see how dynamic memory allocation was possible in c++, thanks.
@themistocles with the vector you can also easily invert the order of the entered numbers, by inserting them at dataArray.begin() instead of end() ;)
std::istream_iterator<int> begin(inputstream), end; std::vector<int> dataArray(begin, end);
@Theolodis while (input >> get) fails on the first invalid input met and breaks the loop. Well, it depends on the use case, but I have a quite generic example at hand.
|
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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.