reading text file to vector os objects

Hello
i'm having trouble while reading the contents of a text file to a vector of objects.

the file has inside it:
Block{ id value origin.x origin.y ending.x ending.y name }
Block{ 7 7 0 0 3 2 aaaaa }
Block{ 7 7 0 3 3 5 aaaaa }
Block{ 7 7 4 0 6 2 aaaaa }
Block{ 7 7 4 3 6 5 aaaaa }
Block{ 7 7 7 0 9 2 aaaaa }
Block{ 7 7 7 3 9 5 aaaaa }
Block{ 7 7 10 0 12 2 aaaaa }
Block{ 7 7 10 3 12 5 aaaaa }
Block{ 7 7 0 7 3 10 aaaaa }


and the code is:
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
#include <iostream>
#include <fstream>
#include <string>
#include <vector>

class Vector2i{
    public:
    int x;
    int y;
};

class Block{
public:
    size_t m_id;
    int m_value;
    Vector2i m_origin;
    Vector2i m_ending;
    std::string m_name;
};

int main()
{
    std::vector<Block> vec;
    std::ifstream file("file.data");
    std::string line;
    getline(file, line); //this is here just to ignore the first line
    while (getline(file, line)){
        if (line.find("Block{ ")!=std::string::npos){
            Block b;
            file >> b.m_id >> b.m_value >> b.m_origin.x >> b.m_origin.y >> b.m_ending.x >> b.m_ending.y >> b.m_name;
            vec.push_back(b);
        }
    }
    file.close();
}


but it creates a single object inside vec, and the values aren't right...
what could i be doing wrong?
thanks in advance! :)
Last edited on
while (getline(file, line))
that'll read the file line by line
so, on the first iteration `line' would have "Block{ 7 7 0 0 3 2 aaaaa }"
then you try to do file >> b.m_id, ¿where are you reading? you are on the next line, "Block"

what you may do is while (getline(file, line, '{')), `line' would have "Block" (the space is discarded) and then file >> b.m_id will read the number
(remember to manage the closing brace)
It would become simpler if you added proper constructors:

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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>

class Vector2i{
public:
    int x {};
    int y {};

    Vector2i() = default;
    Vector2i(int x_arg, int y_arg);

// friend:
    friend std::istream& operator >> (std::istream& is, Vector2i& rhs);
    friend std::ostream& operator << (std::ostream& os, const Vector2i& rhs);
};


Vector2i::Vector2i(int x_arg, int y_arg)
    : x { x_arg }
    , y { y_arg }
{
}


std::istream& operator >> (std::istream& is, Vector2i& rhs)
{
    is >> rhs.x >> rhs.y;
    return is;
}


std::ostream& operator <<(std::ostream& os, const Vector2i& rhs)
{
    os << '{' << rhs.x << "; " << rhs.y << '}';
    return os;
}


class Block {
public:
    std::size_t m_id;
    int m_value;
    Vector2i m_origin;
    Vector2i m_ending;
    std::string m_name;

    Block(std::size_t m_id_arg,
          int m_value_arg,
          Vector2i m_origin_arg,
          Vector2i m_ending_arg,
          std::string m_name_arg);

    Block(const std::string& s);

//friend
    friend std::ostream& operator << (std::ostream& os, const Block& rhs);
};


Block::Block(std::size_t m_id_arg,
             int m_value_arg,
             Vector2i m_origin_arg,
             Vector2i m_ending_arg,
             std::string m_name_arg)
    : m_id { m_id_arg }
    , m_value { m_value_arg }
    , m_origin { m_origin_arg }
    , m_ending { m_ending_arg }
    , m_name { m_name_arg}
{
}


Block::Block(const std::string& s)
{
    std::istringstream iss { s };
    std::string block;      // Block{
    iss >> block >> m_id >> m_value >> m_origin >> m_ending >> m_name;
}


std::ostream& operator << (std::ostream& os, const Block& rhs)
{
    os << "id: " << rhs.m_id
       << "\nvalue: " << rhs.m_value
       << "\norigin: " << rhs.m_origin
       << "\nending: " << rhs.m_ending
       << "\nname: " << rhs.m_name;
    return os;
}


// Block{ id value origin.x origin.y ending.x ending.y name }
// Block{ 7 7 0 0 3 2 aaaaa }
// Block{ 7 7 0 3 3 5 aaaaa }
// Block{ 7 7 4 0 6 2 aaaaa }
// Block{ 7 7 4 3 6 5 aaaaa }
// Block{ 7 7 7 0 9 2 aaaaa }
// Block{ 7 7 7 3 9 5 aaaaa }
// Block{ 7 7 10 0 12 2 aaaaa }
// Block{ 7 7 10 3 12 5 aaaaa }
// Block{ 7 7 0 7 3 10 aaaaa }
int main()
{
    std::ifstream file("file.data");
    std::vector<Block> vec;
    bool first_row { true };
    for(std::string line; std::getline(file, line); /**/) {
        if(first_row) {
            first_row = false;
            continue;
        }
        vec.emplace_back(line);
    }
    file.close();

    for(const auto& e : vec) {
        std::cout << '\n' << e << "\n\n- - -\n";
    }
}


Output:
id: 7
value: 7
origin: {0; 0}
ending: {3; 2}
name: aaaaa

- - -

id: 7
value: 7
origin: {0; 3}
ending: {3; 5}
name: aaaaa

- - -

id: 7
value: 7
origin: {4; 0}
ending: {6; 2}
name: aaaaa

- - -

id: 7
value: 7
origin: {4; 3}
ending: {6; 5}
name: aaaaa

- - -

id: 7
value: 7
origin: {7; 0}
ending: {9; 2}
name: aaaaa

- - -

id: 7
value: 7
origin: {7; 3}
ending: {9; 5}
name: aaaaa

- - -

id: 7
value: 7
origin: {10; 0}
ending: {12; 2}
name: aaaaa

- - -

id: 7
value: 7
origin: {10; 3}
ending: {12; 5}
name: aaaaa

- - -

id: 7
value: 7
origin: {0; 7}
ending: {3; 10}
name: aaaaa

- - -

Hello Stauricus,

The first thing you need to do is make sure the file is open and usable.
1
2
3
4
5
6
7
8
9
10
std::string inFileName{ "Data File.txt" };

std::ifstream file(inFileName);

if (!file)
{
	std::cout << "\n File " << inFileName << " did not open" << std::endl;
	std::this_thread::sleep_for(std::chrono::seconds(3));  // <--- Needs header files chrono" and "thread". Optional.
	return 1;  //exit(1);  // If not in "main".
}

Calling your input file "file.data" leads one to believe that is a special when it is only a text file. As I did you can use "Data File.txt" or "file.data.txt" anything that ends with ".txt".

Your two classes have everything as "public" might as well make them structs because you are defeating the whole point of the class keeping the variables "private".

Line 29 should be defined before the while loop rather than creating a new object of "Block" each time through the while loop.

Line 28 the "line.find" is not being used properly. The function returns a value which is the position of the first match. In this case the "B" in "Block". Again not what you want. What you want to find is the position of the space. Knowing the position of the first space you could take a sub string bypassing the "Block{ " and keeping what is left. Still having a problem with the last space and }. I used ".pop_back()" to remove the last two characters leaving just the numbers and the letters.

The if statement is not working the way you may be thinking. If you read the line in the while condition then remove what you do not need you will be left with just what you need for line 30 to read.

There is more checking I have to do. I will let you know what I find.

Hope that helps,

Andy
Hello Stauricus,

I was so focused on the if statement in the beginning I missed what is really happening.

Line 26 reads the headings and moves on. this leaves the file pointer at the beginning of the second line.

Line 27 reads the second line of the file and leaves the file pointer at the beginning of the third line.

When you reach line 30 you are trying to put "Block{ " into a numeric variable which caused the file stream to fail thus all the rest of your reads do not work.

ne555 has the best suggestion to bypass the beginning of each line.

I did come up with another option. It is not as simple as what ne555 suggested, but it works:
1
2
3
4
5
6
7
8
9
10
11
while (getline(file, line))
{
	size_t pos = line.find(" ");
	size_t len = line.find_last_of(' ');

	std::istringstream ss(line.substr(pos, (len - pos)));  // <--- Needs header file <sstream>

	ss >> b.m_id >> b.m_value >> b.m_origin.x >> b.m_origin.y >> b.m_ending.x >> b.m_ending.y >> b.m_name;

	vec.push_back(b);
}

This works because "ss" only contains "7 7 0 0 3 2 aaaaa".

Sorry for any earlier confusion.

Hope this helps,

Andy
hi! thanks everybody for the answers!

so, then i noticed that the error was in the fact i was trying to add the string "Block{" to integer variables. i did it to fix:

1
2
std::string s;
file >> s >> b.m_id >> b.m_value >> b.m_origin.x >> b.m_origin.y >> b.m_ending.x >> b.m_ending.y >> b.m_name;


so i create a string, add the first value of the line (in this case, "Block{ ") to it, and simply do nothing with it.
but it seems an ugly solution. is there any better way?
i tried
file >> std::cin::ignore() >> b.m_id >> b.m_value >> b.m_origin.x >> b.m_origin.y >> b.m_ending.x >> b.m_ending.y >> b.m_name;
but of course it didn't worked :P

thanks for the help!
Last edited on
Topic archived. No new replies allowed.