Just started learning C++ today. I'm writing a program to keep up with school assignments.

#include <iostream>
#include <stdlib.h>
#include <fstream>
#include <string>

struct Assignment
{
string Period;
string Title;
string Page;
string Date;
string Instruct;
};

void NewAssignment()
{
system("CLS");

Assignment Work;
cout << "New Assignment Entry" << endl;
cout << "--------------------------" << endl << endl;

cout << "Period: ";
getline(cin, Work.Period);

cout << "Title: ";
getline(cin, Work.Title);

cout << "Page Number: ";
getline(cin, Work.Page);

cout << "Date: ";
getline(cin, Work.Date);

cout << "Instructions: ";
getline(cin, Work.Instruct);

WriteInfo(Work); //Writes to a file
}

int main()
{
system("CLS");

int choice;

cout << "Assignment Manager"            << endl << endl;
cout << "1: Add an assignment"          << endl;
cout << "2: Browse saved assignments"   << endl;
cout << "3: Delete an assignment"       << endl;
cout << "4: Exit"                       << endl << endl;

cin  >> choice;

if(choice == 1)
{NewAssignment();}

else if (choice == 2)
{main();}

else if (choice == 3)
{main();}

else if (choice == 4)
{exit(0);}

else{main();}

return 0;
}

It screws up somewhere in the NewAssignment function. When it is called it gives me

New Assignment Entry
------------------------

Period: Title:

It completely skips over getting input for Work.Period. It works right when I use cin but then all of the others do the same, and I must use getline() so spaces will be recognized.

4
Contributors
10
Replies
11
Views
6 Years
Discussion Span
Last Post by WaltP
Featured Replies
• 1

This appears to be the common problem with mixing >> and getline() in the same program. The problem is probably because you call >> on line 53 before you call NewAssignment(), which is where getline() is called. Turns out that >> and getline() don't work the same. One way they …

• 1
WaltP 2,905   6 Years Ago

It says the error is in main on line 61. Might be a good idea to show us that line. Also [iCODE]string filename = Work.Title += ".txt";[/iCODE] should be [iCODE]string filename = Work.Title + ".txt";[/iCODE] I believe. I doubt you want to change [I]Work.Title[/I]

• 2

>> Why doesn't it accept the filename I give it? [URL="http://www.cplusplus.com/reference/iostream/fstream/open/"]open()[/URL] only accepts a [ICODE]const char *[/ICODE], so you have to use [ICODE]c_str()[/ICODE] .. [code] file.open(filename.c_str()); [/code] In your original post, you are calling [ICODE]main()[/ICODE] recursively - it's a poor practice and also forbidden. You could instead devise a regular …

This appears to be the common problem with mixing >> and getline() in the same program. The problem is probably because you call >> on line 53 before you call NewAssignment(), which is where getline() is called. Turns out that >> and getline() don't work the same. One way they differ is that >> can't put spaces in variables and getline() can. The problem is based on the way input is handled in C++. Input in C++ is routinely buffered, meaning the keyboard or other input is stored in an area of memory, called a buffer, and which is probably an array, declared internal to the software. When you hit the enter key the first input method on the stack tries to take information out of the input buffer to store in the variable that you want to use. As you might expect these input methods need some way to know when to stop trying to take information out of the buffer. For >> the signal to stop input is a whitespace character after the first non whitespace char. The terminating whitespace char is left in the buffer and when the next call to >> occurs, >> ignores any leading whitespace characters in the buffer. This is fine as long as all input methods used in the program follow the same protocol. However, getline() doesn't. getline() stops putting input into the variable when it reaches a certain length or when it when it reaches a terminating char. The terminating char can be any legal char, and it defaults to the newline char. Turns out when you push the enter key to signal end of input, call the input method, the enter key is actually interpretted by the program as a new line char. Therefore the call to >> will leave the new line char in the input buffer and when the first call to getline() arrives the first char in the input buffer is the new line char left by the prior call to >> and if the new line char is the terminating char for that call to getline() it assumes there is no information to put in this variable and therefore looks like it skips the variable. By the way getline() removes the terminating char fromt he input buffer. In order to correct this either don't mix calls to input methods that don't deal with the input buffer in the same manner within a given program, or be sure, the best you can, that the input buffer is cleared before you attempt to call getline() and similar input methods that don't ignore leading whitespaces. Usually this is done by placing a call to ignore() after each call to >> or before each call to getline() when you mix the two in the same program. You should send a suitably large int to ignore(), say 1000, although how large ther int should be isn't all that important, usually (some people prefer to use the maximum size of the input buffer, and some people just use some large number).

Edited by Lerner: n/a

VERY descriptive and understandable

Wow. Excellent explanation! It seems to be working fine now thank you.
Now I have a problem with naming the file it saves data to. Right now I just want it to be saved to a text file whose name is whatever Work.Title is.

void WriteInfo(Assignment Work)
{
string filename = Work.Title += ".txt";
cout << filename << endl; //Test to see what output was. My case was "test.txt"

ofstream file;

file.open(filename); //Does not accept the string "test.txt"

file << "Assignment from "  << Teacher(Work) << ", " << Work.Period << PeriodAbbrev(Work) << " Period." << endl << endl;
file << "Title: "           << Work.Title    << endl;
file << "Page : "           << Work.Page     << endl;
file << "Date : "           << Work.Date     << endl;
file << "\nInstructions : " << Work.Instruct << endl;

file.close();
}

Why doesn't it accept the filename I give it? I get these two build errors which make no sense to me.

main.cpp|61|error: no matching function for call to 'std::basic_ofstream<char, std::char_traits<char> >::open(std::string&)'|

c:\mingw\bin\..\lib\gcc\mingw32\4.4.1\include\c++\fstream|696|note: candidates are: void std::basic_ofstream<_CharT, _Traits>::open(const char*, std::_Ios_Openmode) [with _CharT = char, _Traits = std::char_traits<char>]|

Edited by WildBamaBoy: n/a

It says the error is in main on line 61. Might be a good idea to show us that line.

Also string filename = Work.Title += ".txt"; should be string filename = Work.Title + ".txt"; I believe. I doubt you want to change Work.Title

Good eye!

>> Why doesn't it accept the filename I give it?

open() only accepts a const char * , so you have to use c_str() ..

file.open(filename.c_str());

In your original post, you are calling main() recursively - it's a poor practice and also forbidden. You could instead devise a regular do/while loop, for example.

Yep

It says the error is in main on line 61. Might be a good idea to show us that line.

Also
string filename = Work.Title += ".txt"; should be
string filename = Work.Title + ".txt"; I believe. I doubt you want to change Work.Title

My bad, line 61 is line number 8 in the last bit of code I posted.
Good eye with the += thing. I in fact did not want Work.Title changed. That would've took me forever to figure out.

In your original post, you are calling main() recursively - it's a poor practice and also forbidden. You could instead devise a regular do/while loop, for example.

I'm just using calls to main() as placeholders for now but I was not aware of that.
I did however plan to return to the main function when the program has finished a whole operation so the user can choose what else they may want to do. I don't know if this would be common practice or not but would just defining a different function to deal with choices and recursively call it be...acceptable? It seems much less complicated than doing a while loop. :|

Just something like:

void choose()
{
system("CLS");

int choice;

cout << "Assignment Manager"            << endl << endl;
cout << "1: Add an assignment"          << endl;
cout << "2: Browse saved assignments"   << endl;
cout << "3: Delete an assignment"       << endl;
cout << "4: Exit"                       << endl << endl;

cin  >> choice;

if(choice == 1)
{NewAssignment();}

else if (choice == 2)
{choose();} //Will be {browse();}

else if (choice == 3)
{choose();} //Will be {delete();}

else if (choice == 4)
{exit(0);}

else{choose();}
}

int main
{
choose();
return 0;
}

And file.open(filename.c_str()); worked by the way. It saves correctly now!

I also have a question. What exactly is the difference between a string and a const char *?

Edited by WildBamaBoy: Question

Do not use recursion at all. Period. Use a loop.

Consequences of recursion? Just curious.

Probably not...

Erm...what ARE the consequences of recursion? Why should I not do it?