C++ and files: input operation doesn't work

Hello, see this code:

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
int main()
{
	fstream myfile;
	myfile.open("test.txt", ios::out | ios::in | ios::app);

        // APPEND 3 STRINGS TO THE FILE
 	for (int i{ 0 }; i < 3; i++)
	{
		string nome;
		cout << "Inserisci nome: ";
		cin >> nome;
		
		myfile << nome << endl;
	}	
 
        // READ THOSE STRINGS
	string n;

	while (myfile >> n)
	{
		cout << n << endl;
	}

	myfile.close();
	
	_getch();
	return 0;
}


It lets me type 3 strings in the file and it works: in the file I can see those strings.

The problem comes in the while loop. I can't see the output... it just prints nothing.

I tried closing the file and re-opening it only in input mode, and it works.

But I want to use my files both in OUT and IN mode
Last edited on
You need to set the position from which to start the next read operation.
1
2
3
4
5
6
    myfile.seekg (0, myfile.beg);

    while (myfile >> n)
    {
        cout << n << endl;
    }


http://www.cplusplus.com/reference/istream/istream/seekg/

Last edited on
Thanks!

Why should I use seekg in this case?

Maybe because after writing into the file, the 'cursor' lies at the end of it?
Apparently there is just a single pointer used to maintain position for both read and write operations in the file stream. I'm not sure of the best documentation on this.

Here it is discussed:
http://stackoverflow.com/questions/14329261/are-seekp-seekg-interchangeable
Ok thanks.

Last question:

If I have to write from a file to a struct, and this struct has MANY attributes... is this a good way to code?

1
2
3
4
while (file >> result.name >> result.surname >> result.age>> result.x >> result.y >> result. a >> result.b >> result.c >> result.d...)
{
   ...
}


Well, that's workable for a small number of member variables, but it rapidly grows cumbersome. You may like to consider overloading the << and >> operators for output and input streams.

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
#include <iostream>
#include <fstream>
#include <string>

struct Something {
    std::string name;
    std::string surname;
    int age;
    
    friend std::ostream& operator<<(std::ostream & os, const Something & s)
    {
        return os << s.name << '\t' << s.surname << '\t' << s.age;
    }
    
    friend std::istream& operator>>(std::istream & is, Something & s)
    {
        is >> s.name >> s.surname >> s.age;
        return is;
    }
    
    
};

int main()
{
    Something a {"Fred", "Bloggs", 27};
    Something b {"Sue", "Smythe", 42};  
    
    std::string fname ("test.txt");
    
    {
        std::ofstream fout(fname);
        fout << a << '\n'
             << b << '\n';
    }
    
    Something result;
    std::ifstream fin(fname);
    while (fin >> result)
    {
        std::cout << result << '\n';
    }
    
}

output:
Fred    Bloggs  27
Sue     Smythe  42


Oh ok, byt look at this!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// INSIDE THE 'PERSON' STRUCTURE

	// Write a structur into the file
	friend ofstream& operator<<(ofstream& ofs, const Person& p)
	{
		ofs << p.name << " " << p.surname << " " << p.age << endl;
		return ofs;
	}

	// Read the file
	friend ifstream& operator>>(ifstream& ifs, Person& p)
	{
		while (ifs >> p.name >> p.surname >> p.age)
		{			
			cout << "Name: " << p.name << endl;
			cout << "Surname: " << p.surname << endl;
			cout << "Age: " << p.age << endl;
			cout << endl;			
		}

		return ifs;
	}


1
2
3
4
5
6
7
8
9
10
11
12
13
// IN THE MAIN
	char* PATH = "test.txt";
	Person my{ "MyName", "MySurname", 5 };

	ifstream i(PATH);

	cout << "BEFORE: " << my.name << endl;

	i >> my;

	cout << "AFTER: " << my.name;

	i.close();


The test.txt file contains:
1
2
ITALY ITALY 000
LONDON LONDON 999


But the output is:
1
2
BEFORE: MyName
AFTER:  LONDON


Why? I should get:

1
2
BEFORE: MyName
AFTER:  ITALY


Since ITALY is in the first row.
Well --- that's a pretty horrible design. Sorry to say that, I usually don't express myself so strongly.

If you must have a function which uses a while loop to read all the items from the file, make it a separate function. Don't try and stuff it inside some other function where it doesn't belong. Try to keep each function doing just one specific thing, and have it do it cleanly and well. That way, the functions are reusable. A very specialised function which does unexpected things gives, as you've discovered, unexpected results.

Since your friend ifstream& operator>> function loops through the entire file (or until there is an error) after the line i >> my; the variable my will contain the result of the very last successful read operation.




Yeah, the bug was the loop.

I should've only read the first line.

Thanks mate!
Ok, I'm sure you've got this understood by now. I just wanted to add that the way to look at it is as though the entire struct is an object, just like one of the built-in types int or double or a library type such as std::string.

Hence when we do int n; cin >> n; or string name; cin >> name; we know what to expect of the >> operator - it reads a single object of the given type from the stream. Similarly with the output <<. When adding this functionality for our own types (a struct or class), it is a good idea to keep to that pattern.
Topic archived. No new replies allowed.