\n attached to final string in delimited getline?

Hi,

Trying to read in from a file, using delimited getline.

Using the following:

string item;
int numbers[MAXARRAY];
vector<Data> MainVec;
Draw draw1;

ifstream myfile;
myfile.open("testdoc.txt");
if (myfile.is_open())
{
int i = 0;
while ( getline (myfile,item, ','))
{
if (!myfile.good())
{
numbers[i] = stoi(item);
draw1.a = numbers[0];
draw1.b = numbers[1];
draw1.c = numbers[2];
draw1.d = numbers[3];
draw1.e = numbers[4];

MainVec.push_back(draw1);
i=0;
}
else
{
numbers[i] = stoi(item);
i++;
}
}
}


The numbers in the file are in the following format:

12,34,1,19,50
71,80,33,23,2
90,12,21,54,22
etc...
etc..

The problem is that the delimited getline tacks the EOL character onto the final string read in by the getline.
I'm try to get it to detect the EOL character and then load that line of numbers into the class containing these numbers and then push_back into the vector.

I tried the good() function but it either incorporates the EOL into the final element of the array or somehow continues counting the element into the array, thus overstepping the bounds of the array and so throwing a segmentation fault.

I tried swapping an array of fixed length into a vector of its own that shouldn't run out of room, but this developed its own set of problems.

Short of reading in a long string and parsing it myself (done it before but long, painful and fiddly!) is there a way to get around this?

Thanks.
Last edited on
Short of reading in a long string and parsing it myself (done it before but long, painful and fiddly!)

Here's an approach using that method. The most difficult part was figuring out the two classes Draw and Data.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <iostream>
#include <fstream>
#include <iomanip>
#include <string>
#include <vector>
#include <sstream>

class Draw {
public:
    int a;
    int b;
    int c;
    int d;
    int e;
};

std::ostream & operator << (std::ostream & os, const Draw & d)
{
    os << d.a << ' ' << d.b << ' ' << d.c << ' ' << d.d << ' ' << d.e;
    return os;
}

using namespace std;

int main()
{
    vector<Draw> MainVec;

    ifstream myfile("data.txt");
    if (!myfile)
    {
        cout << "Input file not open\n";
        return 1;
    }

    for (string line; getline (myfile,line);   )
    {
        for (char & ch : line)
            if (ch == ',')
                ch = ' ';

        istringstream ss(line);
        Draw draw1;
        if (ss >> draw1.a >> draw1.b >> draw1.c >> draw1.d >> draw1.e)
            MainVec.push_back(draw1);
    }

    for (const auto & a : MainVec)
        cout << a << '\n';
}



Or an alternative, which I prefer as it keeps the complexity out of main() and delegates it to other functions:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <iostream>
#include <fstream>
#include <iomanip>
#include <string>
#include <vector>
#include <sstream>

class Draw {
public:
    int a;
    int b;
    int c;
    int d;
    int e;
};

std::ostream & operator << (std::ostream & os, const Draw & d)
{
    os << d.a << ' ' << d.b << ' ' << d.c << ' ' << d.d << ' ' << d.e;
    return os;
}

std::istream & operator >> (std::istream & is, Draw & d)
{
    char ch;
    is >> d.a >> ch >> d.b >> ch >> d.c >> ch >> d.d >> ch >> d.e;
    return is;
}

using namespace std;

int main()
{
    vector<Draw> MainVec;

    ifstream myfile("data.txt");
    if (!myfile)
    {
        cout << "Input file not open\n";
        return 1;
    }

    Draw draw1;

    while (myfile >> draw1)
        MainVec.push_back(draw1);

    for (const auto & a : MainVec)
        cout << a << '\n';
}


Many thanks!
Your generosity know no bounds!

I will give them both a try.

Thanks again!
Tested them out.
Solution 1 worked flawlessly when applied fully to my application.
Solution 2 gave the first line but nothing else.

I don't understand the code (just getting back into C++) so can't debug it.
Can you tell me what these two stream operators are generally known as so I can read up on them and/or point me to a reference that will explain them so that I may understand them better?

Many thanks.
Thanks for the feedback.

Well, I tested both versions of my code with the following text file:
12,34,1,19,50
71,80,33,23,2
90,12,21,54,22
and each of them worked for me.
If your text file looks different to this (e.g. extra commas or other text), that may be the problem.

In terms of functionality, the key differences are these:
version 1:
read a whole line at a time, 
convert all commas to spaces
create a stringsteam (which works just like an input file)
read five integers from the stream


version 2 does not read line by line, it treats the input file as a stream of consecutive items
read an integer, read a comma
read an integer, read a comma
read an integer, read a comma
read an integer, read a comma
read an integer


Of course, the code for "version 1" could be incorporated into the operator >> function of "version 2", so that each would behave in the same way, apart from the different style.

I used 'operator overloading' of << and >> in the code. Operator overloading is a sub-topic in its own right which is more than I'd want to go into here - you should look it up in a textbook or online tutorial.

There's a brief example here, but you'd probably want to look around for some tutorial which goes into the level of detail which you need.
https://msdn.microsoft.com/en-us/library/1z2f6c2k.aspx

As for the problem of the code not working for you, could you post a complete version (one which can be compiled and run) of your code. You may trim unnecessary extra code, so long as it still runs and still demonstrates the problem.

Also, for legibility, please use code tags <> when posting your code - see article below:
http://www.cplusplus.com/articles/jEywvCM9/


One last comment - on the original code: something like this might work:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    string item[5];
    Draw draw1;
    
    while (getline (myfile, item[0], ',') && 
           getline (myfile, item[1], ',') && 
           getline (myfile, item[2], ',') && 
           getline (myfile, item[3], ',') && 
           getline (myfile, item[4], '\n'))
    {      
        draw1.a = stoi(item[0]);
        draw1.b = stoi(item[1]);
        draw1.c = stoi(item[2]);
        draw1.d = stoi(item[3]);
        draw1.e = stoi(item[4]);

        MainVec.push_back(draw1);        
    }

Last edited on
Thanks again for your help.
I tried them late last night when I was somewhat bleary-eyed so I may have slipped up somewhere.
I'm running this on Ubuntu, by the way.
I understand that the way that Linux handles strings may be slightly different to Windows/Mac.

I agree that the second example is better for modularity of design (which, as we know, is key in C++) so I'll give it some extra attention to make it work.

Thanks for the tip on legibility- I was wondering why my code lost all its formatting and indentation!
I'll post a copy of my code shortly.
I understand that the way that Linux handles strings may be slightly different to Windows/Mac.

Not sure about that. When reading from a file, there may be differences in the line endings, which sometimes causes problems, not sure that would apply here.
Here is the application of your code to my program:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include <iostream>
#include <fstream>
#include <iomanip>
#include <string>
#include <vector>
#include <sstream>

class Draw
{
public:
    int drawnumber;
    long int drawdate;
    int number1;
    int number2;
    int number3;
    int number4;
    int number5;
    int number6;
    int supp1;
    int supp2;
};

std::ostream & operator << (std::ostream & os, const Draw & d)
{
    os << d.drawnumber << ' ' << d.drawdate << ' ' << d.number1 << ' ' << d.number2 << ' ' << d.number3 << d.number4 << d.number5 << d.number6 << d.supp1 << d.supp2;
    return os;
}

std::istream & operator >> (std::istream & is, Draw & d)
{
    char ch;
    is >> d.drawnumber >> ch >> d.drawdate >> ch >> d.number1 >> ch >> d.number2 >> ch >> d.number3 >> ch >> d.number4 >> ch >> d.number5 >> ch >> d.number6 >>
    ch >> d.supp1 >> d.supp2;
    return is;
}

using namespace std;

int main()
{
    vector<Draw> MainVec;

    ifstream myfile;
    myfile.open("testdoc.txt");
    if (!myfile)
    {
        cout << "Input file not open\n";
        return 1;
    }

    Draw draw1;

    while (myfile >> draw1)
        MainVec.push_back(draw1);

    for (const auto & a : MainVec)
        cout << a << '\n';
}


Also, here is an example of the input file:

101,20160101,24,27,3,45,22,19,1,34
102,20160102,33,18,2,7,45,3,28,43
103,20160103,41,3,4,38,37,26,27,6
104,20160104,3,42,4,17,21,37,40,30
105,20160105,40,30,20,44,22,13,41

Having worked with it a bit, I've got it so that it runs but gives no output.
I will read up on Operator Overloading (I need to learn - I don't seriously expect you two write all my code for me, although I do appreciate everything you've done!) so I can better understand your code and apply this method in the future.

Again, many thanks for your assistance!
Line 33:
1
2
3
    // ...
    // ch >> d.supp1 >> d.supp2; // line 33
    ch >> d.supp1 >> ch >> d.supp2;

http://coliru.stacked-crooked.com/a/640b7a7d645755c2
Ah-hah!
A simple, stupid mistake on my part!
Many thanks!

Currently reading up on overloading of operators.
Many thanks for your guidance.
Topic archived. No new replies allowed.