Using a while loop to write to a file

Is it possible to use a while loop to continuously write lines to a file and quit when you're finished? Or do you have to use a for loop?

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

class Data
{
public:
	std::string returnname(std::ifstream& object)
	{
		std::getline(object, name);
		return name;
	}
	std::string getname()
	{
		std::getline(std::cin, name);
		return name;
	}

private:
	std::string name;
};

int i;

int main()
{
	Data data;
	std::ofstream ofile("notes.txt");
	while (ofile.good())
	{
		ofile << data.getname();
	}

	std::ifstream ifile("notes.txt");
	while (ifile.good())
	{
		std::cout << data.returnname(ifile) << std::endl;
	}

	return 0;
}
Last edited on
Is it possible? Yes.

1
2
3
4
5
6
7
8
int main()
{
  std::string line;
  std::ofstream ofile("notes.txt");
  while ( std::getline(std::cin, line) && ofile << line << '\n' )
  {
  }
}
Code works but what I meant was continuously write lines to a txt file. Meaning that I type a line, hit enter and repeat until I am finished.
Whose code?
Last edited on
Your code
Yes, "works as meant". Do you see a problem in there? What kind of problem?
What I would like to know is if it is possible to write to a file like the example below, using a while loop and closing the file whenever you are done writing? What I want to do is give the user the option to enter how many lines they want and when they are done, enter the word "Quit" to come out of the loop.

Example:

Hello my name is Bob
What is your name?
Do you like C++?

Last edited on
Hello nicholasjb1996,

Yes and No.

For an input file a while loop would be the best choice and is generally written as:
while (std::getline(inFile, line)). This way when it tries to read past end of file it sets "eof" causing the while condition to become false and the while loop fails.

For the output stream the while condition would have to be different. Right now you are basing the condition on output stream being good and it will always be good unless you close the stream or something happens with line 31 to change the state bits.

Some other problems I see:

When you open a file for input you need to follow this with a check to make sure the file is open.
1
2
3
4
5
6
7
8
9
10
const std::string inFileName{ "" };

std::ifstream inFile("inFileName");

if (!inFile)
{
	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". Also optional.
	return 1;  //exit(1);  // If not in "main".
}

The if statement is the most important part and as the comment says line 8 is optional. I need it for the way my IDE is setup. line 9 the return is for when this is used in "main" and the "exit" is used if you put this in another file.

Then there is the point that you are writing to your output file before you read anything from your input file. Line 31 could work better if it followed line 37 for what you are doing.

You have a nice class, but you are not using it to it full potential. When you read the input file and store the name in the class variable "name" it would be a good idea to store the class in a vector of type "Data" for later use. This way you could use a while loop or a for loop to write to the output file.

Almost forgot when opening a file for output the check to see if the stream is open in not always necessary, but a good idea. When opening a file stream for output is the file does not exist it will create it. So the only thing to cause a problem is if the file name is wrong.

I will say that this is an interesting problem to figure out. I have to see what I can come up with and let you know.

Hope that helps,

Andy
Hello nicholasjb1996,

After seeing keskiverto's response I see that I had a total misunderstanding of what you wanted. Sorry about that.

Think about his code and what you want to do and what his code is doing. It would make sense to check if what was entered is "Quit" or "quit" and beak out of the while loop.

Andy

Thanks for the reply Andy, I believe I understand the majority of what you wrote. As for the class that I created, I was not thought the topic classes yet in my lectures but I am learning ahead for my understanding, so the idea of using a vector to store the class in has not occurred to me. To clarify what I am unsure of, when you said "For an input file a while loop would be the best choice and is generally written as:" do you mean input as in reading from a file to the console? And the other problems you see, is it that I did not do a check and that I should check to make sure that the file is open?
Hello nicholasjb1996,

Yes. The example I gave is for reading from a file and not the keyboard.

What I said was based on the code you posted. Part of that is checking that the input file stream did open the file and is good. Part was based on trying to write to the output file before there is anything to write and using a while loop to write to the output file when there is only one object of the class that contains nothing. So you would have an endless loop writing nothing to you output file.

Then there is the part that reading the input file the class only deals with one read and the next overwrites whatever you had with a new name. Hence storing the class in a vector for later use.

After I pressed "Submit" I realized you are using the same file name for both input and output. This may work, but you need to read the file first and then close the file stream before you open it for output. Otherwise you would have a conflict trying to open a file that is already open.

Hope that helps,

Andy
If possible could you help me out with understanding how to check what was entered and exit the loop. I have been trying and each time I enter "Quit" the program would just prompt for another input until it somehow quits. Then when I open the file from the computer I see that only one line was stored in the file. However if I use a for loop the program reads in all the lines and when I check the file after, it has all the lines that I entered is stored in the file
Hello nicholasjb1996,

What does you code look like now?
Hello nicholasjb1996,

Looking at keskiverto's code The while condition could easily have been written with a semi-colon at the end of the line. I believe that he left the empty {}s to tell you that you need to put something between them.

1
2
3
4
5
6
7
8
9
int main()
{
	std::string line;
	std::ofstream ofile("notes.txt");

	while (std::getline(std::cin, line) && ofile << line << '\n')
	{
	}
}


Looking at the while condition[code]std::getline(std::cin, line)[/ code] stores in the input buffer what you type until until you type "Enter" Then it is transfered to the variable "line". What "std::cin" gathers and puts into a "std::string" makes it very hard to cause "std::cin" to fail. So far the only way I have found out of the while loop is to type "Ctrl + z + Enter".

The empty{}s allows you to check if "line" is equal to quit with an if statement.It also allows the possibility to call a function to change every letter of what was typed to say lower case letters before you check for "quit". You could even move writing to the output file inside the{}s.

When you think about what you want to do it is to enter something until you type "quit", so after you type "quit" you need to check for this. If (hint hint) you find that "quit" was entered you can use the break statement to leave the while loop.

I was hoping I could see your latest code before I finished writing this. Looks like I may have to wait until tomorrow.

Hope that helps,

Andy
when they are done, enter the word "Quit" to come out of the loop.

That is vital decision. The "how to end".

One method is "End of File" character. Every (text) file has one.
If you redirect a file to program's standard input when you call the program, then std::cin will eventually reach the end of file.
On Windows you can generate EOF with Ctrl-z. On Unix-like systems Ctrl-d sends EOF.

The way I wrote my code, the loop will end
IF cin gets EOF
OR write to ofile fails

You want an additional condition for the "end":
end loop
IF cin gets EOF
OR input was "Quit"
OR write to ofile fails

I would wrap that in a function:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// returns false, if text means "End of input"
bool valid( const std::string & text )
{
  return text != "Quit"; // modify to your liking
}

int main()
{
  std::string line;
  std::ofstream ofile("notes.txt");

  while ( std::getline(std::cin, line) && valid(line) && ofile << line << '\n' )
  {
    // empty body. All processing occurs in the condition.
  }
}
Hello Andy and keskiverto, thank you both for replying. I was unable to continue my work last night unfortunately but I just tried the code that keskiverto coded and it worked perfectly. However I would like to understand how exactly the compiler interprets the condition for the while loop. I am also confused by the function bool valid, why is it returning != "Quit" and how does that affect the while loop. From what I understand the while loop runs while the conditions getline and valid and ofile and line and newline are all true. This is what I have so far and it is working perfectly as I said.

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

class Data
{
public:
	std::string returnname(std::ifstream& object)
	{
		std::getline(object, name);
		return name;
	}
//	std::string getname() 
//	{
//		std::getline(std::cin, name);	//This function is no longer needed?
//		return name;
//	}
	bool valid(const std::string name)
	{
		return name != "Quit";
	}
	std::string loop(std::ofstream& object)
	{
		while (std::getline(std::cin, name) && valid(name) && object << name << '\n')
		{
			
		}
		return name;
	}

private:
	std::string name;
};

int i;

int main()
{
	Data data;
	std::ofstream ofile("notes.txt");
	if (ofile.is_open())
	{
		std::cout << "File Is Open For Writing " << std::endl;
	}
	else
	{
		std::cout << "Error Occurred " << std::endl;
	}
	data.loop(ofile);
	ofile.close();

	std::ifstream ifile("notes.txt");
	while (ifile.good())
	{
		std::cout << data.returnname(ifile) << std::endl;
	}
	ifile.close();

	return 0;
}
1
2
while (std::getline(std::cin, name) && valid(name) && object << name << '\n')
{}

has a condition that contains logical and (&&).
1
2
3
4
5
6
7
8
bool result = A && B;
// achieves same as:
bool result = false;
if ( A ) {
  if ( B ) {
    result = true;
  }
}

Note that if expression A is false, then the expression B is not evaluated.
From what I understand the while loop runs while the conditions getline and valid and ofile and line and newline are all true.

Indeed.
IF std::getline(std::cin, name) was successful
AND valid(name) is true
AND object << name << '\n' was successful

The getline returns a reference to the stream:
http://www.cplusplus.com/reference/string/string/getline/
therefore,
1
2
3
4
if ( std::getline(std::cin, name) )
// is almost like
std::getline(std::cin, name);
if ( std::cin )


The operator<< returns a reference to the stream too:
http://www.cplusplus.com/reference/string/string/operator%3C%3C/
http://www.cplusplus.com/reference/ostream/ostream/operator-free/

How does std::cin or ofile turn into a bool?
http://www.cplusplus.com/reference/ios/ios/operator_bool/

If an I/O operation has failed, then the stream has its fail-bit set and the above operator returns false.


1
2
3
4
bool valid( const std::string & name )
{
  return name != "Quit";
}

The return returns a value.
The statement has an expression name != "Quit" that evaluates to a value.

std::string can be compared to string literal: http://www.cplusplus.com/reference/string/string/operators/

The expression essentially says that if the name is not Quit, then expression is true.

One could have extra code in there:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
bool valid( const std::string & name )
{
  bool invalid = (name == "Quit");
  return not invalid;
}

bool valid( const std::string & name )
{
  bool good = true;
  if ( name == "Quit" ) good = false;
  else if ( name == "quit" ) good = false;
  else if ( name == "-1" ) good = false;
  else if ( name == "bye" ) good = false;

  return good;
}




PS.
Why do you have class Data? You don't need it.
Why does your Data::loop() return a string? You don't use it. What is the value of that string anyway?
Thank you for the explanation. I believe I understand now. I just created a class and made those functions to practice and test limitations of what I can do for future purposes. The string in the loop function is there since the function type needs to return a string. Unless I can use a void function and achieve the same result.
Last edited on
Topic archived. No new replies allowed.