Collecting input from file

I haven't programmed in C++ in a long time and now I'm back in school and taking an intermediate C++ class. I have to do two tasks for my first assignment and I've finished the first one. I'm stuck with the second task.

I have to obtain input from a file called datefile.txt and it should be in a function called readDatefromFile(). I wrote something that could run, but the date will not show and instead it just displays "Sorry, that is not a valid date."

This is absolutely wrong I believe, but this is what I got out of the long period of time of reading and watching videos regarding reading and writing files. It's been forever so I'm very rusty at this.

my readDatefromFile is in the very bottom and there's no date!


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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
#include <iostream>
#include <iomanip>
#include <fstream>


using namespace std;

// The program is to have five functions including main.

int numberOfDaysInMonth(int, int, int);
bool dateIsValid(int, int, int);
void isALeapYear(int);
void dayOfTheYear(int, int, int);
void readDateFromFile(fstream);


int main()
{



  int year, month, day;
  char c;
  bool validInput = false;

    while ((cin) && !validInput)
    {
    cout<<"Date read: ";
    readDateFromFile(string inFile);


    }

    numberOfDaysInMonth(year, month, day);
    dateIsValid(year, month, day);
    isALeapYear(year);
    dayOfTheYear(year, month, day);

    return 0;

}


// numberOfDaysInMonth function

int numberOfDaysInMonth(int year, int month, int day)
{
  bool validInput;
  int DaysInMonth = 0;
  switch (month)
    {
        case 1:
        case 3:
        case 5:
        case 7:
        case 8:
        case 10:
        case 12:
        DaysInMonth = 31;
            break;

        case 4:
        case 6:
        case 9:
        case 11:
        DaysInMonth = 30;
        break;

    }

  if (day < 1 || day > DaysInMonth)
  validInput = false;
  else validInput = true;

if (!validInput)
  {

    cout << "Sorry, that is not a valid date" << endl;
    string garbage;
    getline (cin, garbage); // discard the rest ofthe input line
    }

if (!cin)
{
  cout<<"Could not obtain valid input."<<endl;
  return -1;
}
}

// dateIsValid function

bool dateIsValid(int year, int month, int day)
{
  bool validInput = false;

  if (year<1582)
  validInput = false;
  else if(year==1582 && month < 10)
  validInput = false;
  else if (year == 1582 && month == 10 && day < 15)
  validInput = false;
  else if (month < 1 || month > 12)
  validInput = false;

}

// isALeapYear function
void isALeapYear(int year)
{
  int DaysInMonth = 0;
  switch (year)
  {
  case 2:
  if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0))
  DaysInMonth = 29;
  else
  DaysInMonth = 28;
  }
}

// dayOfTheYear function
void dayOfTheYear(int year, int month, int day)
{
  int sum = 0;
  for (int m = 1; m < month; ++m)
  {
  int monthLength = 0;
  switch (m)
    {
        case 1:
        case 3:
        case 5:
        case 7:
        case 8:
        case 10:
        case 12:
        monthLength = 31;
            break;

        case 4:
        case 6:
        case 9:
        case 11:
        monthLength = 30;
        break;

        case 2:
            if (((year % 4 == 0) && (year % 100 != 0))|| (year % 400 == 0))
        monthLength = 29;
            else
        monthLength = 28;
    }
    sum += monthLength;
  }

// Then add the day number to that sum
int dayNum = sum + day;
    cout << setw(2) << setfill('0') << month << "/" << setw(2) << setfill('0') << day << "/" << setw(4) << year;
    cout << " is day #" << dayNum << " of that year." << endl;

}

void readDatefromFile(ifstream inFile)
{

    ifstream inFile;

    inFile.open("datefile.txt");

    if(inFile.fail())
        cout<<"The file cannot open."
    else
    {
        string s;
        while(inFile>>s)
        {
            cout<<s<<endl;
        }
    }



}



Last edited on
Hello tavil002,

Give a sample of what is in the "datefile.txt" file. Without knowing what the input file looks like it akes it hard to say if the while loop will work. Something you might try: while (std::getline(infile, s)). If there is any white space in the date what you have will not work.

I know it is a small point, but a better name for "s" would make the read function easier to understand.

Hope that helps,

Andy
Thanks Andy.

I do have a question though. Since I'm obtaining the date from a file, what would be the best thing to do to incorporate the other functions to have the same functionality on the date in the text file the same way they would work with a user inputting date directly from cin??
Hello tavil002,

My first thought is when reading the file put each date in a vector of strings for later use. Otherwise you will need to rewrite the whole "readDatefromFile" function moving the beginning of the function dealing with the file stream to main and pass the handle to the function so it will read one date at a time.

The simpler fix would be to pass a vector by reference and load the vector in the while loop. this way you could use it anywhere in the program. I would define the vector in main.

I was looking at the function"isALeapYear". I noticed that "DaysInMonth" is defined in the function and set to either 28or 29, but this variable is destroyed when the function ends and can never be used. Either the function needs to return "DaysInMonth" or this variable should be passed by reference. Also the switch is overkill plus you are switching on "year" which would be a 2 or 4 digit number, but the case is only "2".
I would like to get the read working before taking on the rest of the program. Until you have a good date to work with the rest of the program does not mean much yet.

I would still like to know what the "datefile.txt" file looks like so I can better understand what needs to be done.

Hope that helps,

Andy
The datefile.txt is actually just a date with the format YYYY-MM-DD. The instruction given by my professor was very vague or maybe it's me who does not understand. Perhaps i should explain so you know what I'm trying to do with this program.

Professor provided a code for us to improve and it was all written in main. To make it "prettier" we had to separate it in functions (numberofDaysInMonth, dateIsValid, isALeapYear, dayOfTheYear) and when you would run it, it would ask for user input for a date in YYYY-MM-DD format and it would say what day of the year it is.

I don't think we are supposed to change the code itself apart from rearranging, adding functions, and calling them in main.

Anyway, say if a user would input 2017-12-31 then it would say it's #365 day of that year. It must be also be Gregorian so it does all these checks to see whether it is Gregorian or not. If the date is invalid then it will say "Sorry, that is not a valid date."

But for the second task, instead of having user input, we're going to collect input from a file called "datefile.txt" and read the date off of that, and do the same checks to say what day of the year it is. I have trouble with that and completely confused how to put it together.
I forgot to add that the professor expects this functionality to be implemented in readDateFromFile() and readDateFromFile() in the main function.
Hello tavil002,

Give me a chnce to load up your program and see what happens.

Andy
Sure thing,

Thanks!
Hello tavil002,

The instruction given by my professor was very vague or maybe it's me who does not understand.

Could be both, but I have had professors that were vague just so they could change things later and then try to tell you that the way it is in the real world.

Professor provided a code for us to improve

To me this means do what is needed to make it work even if it means changing the code.

I have come up with this for now. See what you think. There are some parts of the code where I made comments. Be sure to look at them.

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
#include <iostream>
#include <iomanip>
#include <fstream>
#include <string>  // <--- Left out.

int numberOfDaysInMonth(int, int, int);
bool dateIsValid(int, int, int);
void isALeapYear(int);
void dayOfTheYear(int, int, int);
bool readDateFromFile(std::ifstream&, std::string& date);  // <--- Changed.
bool SplitDate(std::string date, int& year, int& month, int& day);  // <--- Added, but could be done in main.

int main()
{
	int year{ 0 }, month{ 0 }, day{ 0 }, lc{ 1 };
	//char c;  // <---Not used yet.
	std::string date{ "" };  // <--- Added.
	bool validInput = false;
	bool validRead{ true };  //<---Added.

//  Moved to main and changed a little.
	std::ifstream inFile;
	std::string iFileName{ "datefile.txt" };
	inFile.open(iFileName);

	if (inFile.fail())
	{
		std::cout << "The file " << iFileName << " did not open." << std::endl;
		std::this_thread::sleep_for(std::chrono::seconds(3));  // <--- Requires header files "chrono" and "thread".
		exit(1);
	}

//************** For User Input *********************
// This works.
	//std::cout << "\n Enter a date: ";
	//std::cin >> date;

	//std::cout << "\n Date read: " << date << std::endl;
	//validInput = SplitDate(date, year, month, day);  // <--- This function could be done here if needed, But needs to be done.

	//************* For File Input **********************
	validRead = readDateFromFile(inFile, date);  // <--- Here once because the function only reads one line.
	validInput = SplitDate(date, year, month, day);

	// Working to this point.
}  // End of main

bool readDateFromFile(std::ifstream& inFile, std::string& date)
{

	inFile >> date;

	if (!inFile)
		return false;

	std::cout << "Date read: ";
	std::cout << date << std::endl;
	//}


	return true;
}

// This could be done in main if needed, but works better in a function.
bool SplitDate(std::string date, int& year, int& month, int& day)
{
	if (date.length() == 10)
	{
		year = stoi(date.substr(0, 4));
		month = stoi(date.substr(5, 2));  // <--- Changed to 5.
		day = stoi(date.substr(8, 2));  // <--- Changed to 8.
		std::cout << std::endl;
		if (year && month && day)
			return true;
	}
	else
		std::cout << "\n Invalid date format. Use 2017-01-31" << std::endl;

	return false;
}


This is an idea of what you could do. Not the best and could be improved on, but i works.

The way I set up the read file function is based on needing the date read from the file back in main for later processing.

With a program like this I like to get the read function working first before moving on to the other functions.

Hope this helps,

Andy

Edit: Changed lines 70 and 71.
Last edited on
Hello tavil002,

As I worked through the functions I realized the whole program flow is wrong.For a start :

1
2
3
4
daysInMonth = numberOfDaysInMonth(year, month, day); // <--- Chnged.
validDate = dateIsValid(year, month, day);  // <--- Changed.
isALeapYear(year, month, daysInMonth);
dayOfTheYear(year, month, day);


The function "numberOfDaysInMonth" has no real use at this point. Unless you add some code to say something about the number of days in the month.

The function "dateIsValid" should be preceeded by a function call to heck if "day" is a valid day.

The function "isALeapYear" has no real use in main and would work better by just returning a bool.

The function "dayOfTheYear" is where the real work is done anf where other functions should be called from.

It would be helpful if I could see the original code before you broke it up into functions to better understand what is needed.

For now I need to rethink how the functions work and do some testing.

Hope that helps,

Andy
I'd say function numberOfDaysInMonth() should call function isALeapYear()

Function dateIsValid() should call numberOfDaysInMonth()

Function dayOfTheYear() should call numberOfDaysInMonth()
@Chervil

I figured that out too, but these are not the only problems that need addressed.

I will cover them shortly.

Andy
@Andy. Of course you are right. I did have a look at the code and came up with a version that worked - in a fashion. But I wasn't entirely sure of the original requirements. Anyway, I'll leave it in your hands - you seem to have this covered.
Hello tavil002,

After working with the other functions this is what I came up with. Stting in main:

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
#include <iostream>
#include <fstream>
#include <string>
#include <iomanip>
#include <limits>  // <--- For std::cin.ignore.
#include <chrono>  // <--- For the sleep code used in checking for file not open.
#include <thread>  // <--- For the sleep code used in checking for file not open.

// The program is to have five functions including main.

int numberOfDaysInMonth(int, int);
bool dateIsValid(int, int, int);
bool isALeapYear(int year, int month);
void dayOfTheYear(int, int, int);
bool readDateFromFile(std::ifstream&, std::string& date);  // <--- Changed.
bool SplitDate(std::string date, int& year, int& month, int& day);  // <--- Added, but could be done in main.
bool ValidateMonthAndDay(const int year, int& month, int& day);

int main()
{
	// Always initialize your variables.
	int year{ 0 }, month{ 0 }, day{ 0 };
	int daysInMonth{ 0 };
	//char c;  // <---Not used yet.
	std::string date{ "2017-09-08" };
	bool validInput = false;
	bool validRead{ true };   // <---Added.
	bool validDate{ false };  // <--- Added.
	bool validDay{ false };  // <--- Added.

	// Better ways to do this, but good for learning.
	std::ifstream inFile;
	std::string iFileName{ "datefile.txt" };

	inFile.open(iFileName);

	// Always a good idea to make sure the file has opened.
	if (inFile.fail())
	{
		std::cout << "The file " << iFileName << " did not open." << std::endl;
		std::this_thread::sleep_for(std::chrono::seconds(3));  // <--- Requires header files "chrono" and "thread".
		exit(1);  // <--- Because there is no point in contiueing.
	}

	//************** For User Input *********************
	//std::cout << "\n Enter a date: ";
	////std::cin >> date;
	//std::cout << date << std::endl;  // <--- Used for testing. Comment out and uncomment above for use.

	//std::cout << "\n Date read: " << date << std::endl;
	//validInput = SplitDate(date, year, month, day);

	//************* For File Input **********************

	while (readDateFromFile(inFile, date))  // <--- Used when input is form file. Will process one date at a time.
	{
		validInput = SplitDate(date, year, month, day);

		//daysInMonth = numberOfDaysInMonth(year, month, day); // <--- Chnged. Not needed here.

		validDay = ValidateMonthAndDay(year, month, day);

		validDate = dateIsValid(year, month, day);  // <--- Changed.

		//isALeapYear(year, month, daysInMonth);  // <---- Not the place to use this here.

		dayOfTheYear(year, month, day);
	}
}


This is what is contained in main. Not quite the way I would have done this, but it does work with what you started with. The return values are not used, but could be expanded on.

To the 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
int numberOfDaysInMonth(int year, int month)
{
	int DaysInMonth = 0;
	switch (month)
	{
	case 1:
	case 3:
	case 5:
	case 7:
	case 8:
	case 10:
	case 12:
		DaysInMonth = 31;
		break;

	case 4:
	case 6:
	case 9:
	case 11:
		DaysInMonth = 30;
		break;
	case 2:
		if (isALeapYear(year, month))
			DaysInMonth = 29;
		else
			DaysInMonth = 28;
		break;
	}

	return DaysInMonth;
}


I changed this to make it a more generic function. Now as it should it only does one thing when needed.

The function "dateIsValid" I only changed the definition of "validInput" from false to true. I think it works properly. Although you might want to use the return value in main to address a return value of false.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
bool isALeapYear(int year, int month)  // <--- Added 2nd parameter. And in prototype.
{
	//int DaysInMonth = 0;
	switch (month)  // <--- Changed.
	{
	case 2:
		//if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0))
		if ((year % 4 == 0) && (year % 100 != 0 || year % 400 == 0))
			return true;

	}

	return false;
}


The switch is not really needed just the if statement. The if statement that is commented out is your original code the other one is what I have used for about a year. Notice the difference in the placement of the () in mine. Not sure if yours will work. I have not tested it since I got the program working. I do know in the beginning it was telling me that this year is a leap year.

1
2
3
4
5
6
7
8
9
10
11
void dayOfTheYear(int year, int month, int day)
{
	int sum{ 0 }, dayNum{ 0 };
	int monthLength = 0;

	for (int m = 1; m < month; ++m)
	{
		monthLength = numberOfDaysInMonth(year, m);

		sum += monthLength;
	}


The changed part of this function. The switch that you had is just a duplicate of what the "numberOfDaysInMonth" function does, so just use it.

The "readDateFromFile" and " SplitDate" functions I covered in an earlier message. Those functions have not chnged.

This is the last function I added to the 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
bool ValidateMonthAndDay(const int year, int& month, int& day)
{
	bool validDay{ false }, validMonth{ false };
	//day = 34;
	//month = 13;

	do
	{
		if (month < 1 || month > 12)
			validMonth = false;
		else
			validMonth = true;

		if (!validMonth)
		{
			std::cout << "\n Sorry, " << month << " is not a valid month. Months are 1 - 12" << std::endl;
			std::cout << " Enter a valid month: ";
			std::cin >> month;
			if (!std::cin)
			{
				std::cin.clear();
				std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
			}
			std::cout << std::endl;
		}
	} while (!validMonth);

	do
	{
		if (day < 1 || day > numberOfDaysInMonth(year, month))
			validDay = false;
		else
			validDay = true;

		if (!validDay)
		{
			std::cout << "Sorry, " << day << " is not a valid day. Days are 1 - 31" << std::endl;
			std::cin >> day;
			if (!std::cin)
			{
				std::cin.clear();
				std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
			}
		}

	} while (!validDay);

	return (validMonth && validDay) ? true : false;
}


This should give you something to work on for awhile. Any questions let me know.

Hope this helps,

Andy
Last edited on
I appreciate this Andy! Thank you very much :)
Topic archived. No new replies allowed.