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?

Nathaniel Ford
  • 20,545
  • 20
  • 91
  • 102
themistocles
  • 25
  • 2
  • 5
  • 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. – JBentley Jul 08 '14 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`. – Christian Hackl Jul 08 '14 at 18:44
  • 1
    [dynamic-arrays] is not a tag for dynamically *allocated* arrays, but dynamically resizing at run-time without explicit memory allocation. – crashmstr Jul 08 '14 at 18:46
  • 1
    For starters: Use `std::vector 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](http://stackoverflow.com/questions/24504582/test-whether-stringstream-operator-has-parsed-a-bad-type?noredirect=1#comment37965807_24504582) – πάντα ῥεῖ Jul 08 '14 at 18:48
  • 1
    @crashmstr: Er, why's that? – Lightness Races in Orbit Jul 08 '14 at 18:51
  • @crashmstr: What the hell are you smoking? `new T[size]` has always had legal run-time sizes... – Puppy Jul 08 '14 at 18:52
  • @LightnessRacesinOrbit Doh. I should just go back to bed. – crashmstr Jul 08 '14 at 18:52

2 Answers2

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.
Theolodis
  • 4,977
  • 3
  • 34
  • 53
  • @πάνταῥεῖ what? sorry, I was editing my question... I did provide a fast answer and edited to add the good answer... – Theolodis Jul 08 '14 at 18:49
  • 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 Jul 08 '14 at 18:51
  • @themistocles with the vector you can also easily invert the order of the entered numbers, by inserting them at `dataArray.begin()` instead of `end()` ;) – Theolodis Jul 08 '14 at 18:52
  • 1
    `std::istream_iterator begin(inputstream), end; std::vector dataArray(begin, end);` – Mooing Duck Jul 08 '14 at 19:44
  • @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](http://ideone.com/VnVGcY) at hand. – πάντα ῥεῖ Jul 08 '14 at 19:56
  • @πάνταῥεῖ to be honest, what I do usually tend to use is boost spirit when it comes to those jobs, so I didn't know. But thank you a lot for the read. – Theolodis Jul 09 '14 at 04:55
  • @πάνταῥεῖ I added an example that won't fail, supposing the whole file has been read into a string. – Theolodis Jul 09 '14 at 06:46
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
πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190