PPP2 Chapter 15 - Can't Open File in Japanese Age Program

As the title says, for whatever reason, I can't open the japanese-age-data.txt file for reading in the Japanese age graphing program at the end of Chapter 15 in the book. I have the .txt file in the same folder as the main .cpp code file, and I also made sure to name the file correctly in the program. So why can't I open it?

Here's the code, for reference:
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
// Osman Zakir
// 6 / 22 / 2017
// Bjarne Stroustrup: Programming: Principles and Practice Using C++ 2nd Edition
// Chapter 15 Example code
// Japanese Ages Graphing Example

#include "../../Graph.h"
#include "../../Window.h"
#include "../../Simple_window.h"
#include <iostream>
#include <fstream>

struct Distribution
{
	int year, young, middle, old;
};

class Scale
	// data value to coordinate conversion
{
	int cbase;		// coordinate base
	int vbase;		// base of values
	double scale;
public:
	Scale(const int b, const int vb, const double s) : cbase{ b }, vbase{ vb }, scale{ s } { }
	int operator()(int v) const { return cbase + (v - vbase) * scale; }
};

std::istream &operator>>(std::istream &is, Distribution &d);

int main()
{
	using namespace std;
	using namespace Graph_lib;

	try
	{
		constexpr int xmax = 600;	// window size
		constexpr int ymax = 400;

		constexpr int xoffset = 100;	// distance from left-hand side of window to y axis
		constexpr int yoffset = 60;		// distance from bottom of window to x axis

		constexpr int xspace = 40;		// space beyond axis
		constexpr int yspace = 40;

		constexpr int xlength = xmax - xoffset - xspace;	// length of axes
		constexpr int ylength = ymax - yoffset - yspace;

		constexpr int base_year = 1960;
		constexpr int end_year = 2040;

		constexpr double xscale = double(xlength) / (end_year - base_year);
		constexpr double yscale = double(ylength) / 100;

		Scale xs{ xoffset, base_year, xscale };
		Scale ys{ ymax - yoffset, 0, -yscale };

		Graph_lib::Window win{ Point{100, 100}, xmax, ymax, "Aging Japan" };

		Axis x{ Axis::x, Point{xoffset, ymax - yoffset}, xlength,
			(end_year - base_year) / 10, "year    1960    1970    1980    1990    "
			"2000    2010    2020    2030    2040" };
		x.label.move(-100, 0);

		Axis y{ Axis::y, Point{xoffset, ymax - yoffset}, ylength, 10, "% of population" };

		Line current_year{ Point{xs(2008), ys(0)}, Point{xs(2008), ys(100)} };
		current_year.set_style(Line_style::dash);

		string file_name = "japanese-age-data.txt";
		ifstream ifs{ file_name };
		if (!ifs)
		{
			error("can't open ", file_name);
		}

		Open_polyline children;
		Open_polyline adults;
		Open_polyline aged;

		for (Distribution d; ifs >> d;)
		{
			if (d.year < base_year || d.year > end_year)
			{
				error("year out of range");
			}
			if (d.young + d.middle + d.old != 100)
			{
				error("percentages don't add up");
			}
			const int x = xs(d.year);
			children.add(Point{ x, ys(d.young) });
			adults.add(Point{ x, ys(d.middle) });
			aged.add(Point{ x, ys(d.old) });
		}

		Text children_label{ Point{20, children.point(0).y}, "age 0-14" };
		children.set_color(Color::red);
		children_label.set_color(Color::red);

		Text adults_label{ Point{20, adults.point(0).y}, "age 15-64" };
		adults.set_color(Color::blue);
		adults_label.set_color(Color::blue);

		Text aged_label{ Point{20, aged.point(0).y}, "age 65+" };
		aged.set_color(Color::dark_green);
		aged_label.set_color(Color::dark_green);

		win.attach(children);
		win.attach(adults);
		win.attach(aged);

		win.attach(children_label);
		win.attach(adults_label);
		win.attach(aged_label);

		win.attach(x);
		win.attach(y);
		win.attach(current_year);

		gui_main();
	}
	catch (const runtime_error &rte)
	{
		constexpr int xmax = 600;	// window size
		constexpr int ymax = 400;
		Graph_lib::Window win{ Point{ 100, 100 }, xmax, ymax, "Aging Japan" };

		Text err_msg_start{ Point{(xmax / 2) - 100, (ymax / 2)}, "Runtime error: " };
		std::size_t err_msg_start_length = err_msg_start.label().length();
		Text err_msg{ Point{(xmax / 2) + err_msg_start_length - 10, (ymax / 2)}, rte.what() };

		err_msg_start.set_color(Color::black);
		err_msg.set_color(Color::black);

		win.attach(err_msg_start);
		win.attach(err_msg);

		gui_main();
	}
}

std::istream &operator>>(std::istream &is, Distribution &d)
	// assume format: (year: young middle old)
{
	char ch1 = 0;
	char ch2 = 0;
	char ch3 = 0;
	Distribution dd;

	if (is >> ch1 >> dd.year >> ch2 >> dd.young >> dd.middle >> dd.old >> ch3)
	{
		if (ch1 != '(' || ch2 != ':' || ch3 != ')')
		{
			is.clear(std::ios_base::failbit);
			return is;
		}
	}
	else
	{
		return is;
	}
	d = dd;
	return is;
}


Please help me out here, anyone who might be able to tell what's wrong. Thanks in advance.
Last edited on
Why don't you use perror on line 75, then you would know what the error is.
Another option is using error(strerror(errno));
http://www.cplusplus.com/reference/cstdio/perror/
Last edited on
Apparently there's "No such file or directory". But how could that be when I know it's right there? This is why I'm asking for help. What reason could there be for it being unable to find it?
Last edited on
I have the .txt file in the same folder as the main .cpp code file, and I also made sure to name the file correctly in the program.

Maybe your .exe is being created into some subfolder or other folder like "Debug" or "Release"?
You could try to put the file in a other directory - like C:\Temp and see if it makes a difference. Of course you need to use an absolute file path.
The program isn't running in the directory where the source file is located. Use getcwd() to find out where the program runs. Then put the file there.
Actually, I know where the .exe file is. So I need to put the .txt file where the .exe file is?

Edit: I tried putting it there, but I still get the same error message. And mentioning the name of the file along with the full path gives me an "Invalid argument" error.
Last edited on
I have the .txt file in the same folder as the main .cpp code file, and I also made sure to name the file correctly in the program.

If you're using an IDE then the input file may need to be in the same directory as the project files, which is not necessarily the same directory as the .cpp file.

But you should be able to run the program from the command prompt, in which case the file must be in the directory from where you are trying to run the program.

And mentioning the name of the file along with the full path gives me an "Invalid argument" error.

Show the code, using absolute addressing should work if you're doing things correctly. And remember if you try to use the Windows Path separator '\' in a string you need to use two of them since C++ considers this character the "escape" character.

By the way how are you verifying that the file exists in the directory in question? IMO, you really should use the command line to verify that the file is in the required directory since Windows is known to "help" you by adding the file extension for you, so that sometimes you end up with files like myText.txt.txt if you're not careful.

Yeah, I was also worried about the "myText.txt.txt" thing, but I wasn't sure how to check until now. Thanks for the Command Prompt advice. I was able to confirm in the Command Prompt that the file is really called "japanese-age-data.txt.txt". The darned thing really got me. Anyway, that problem's been fixed now. But now I have another problem.

Here's the 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
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
// Osman Zakir
// 6 / 22 / 2017
// Bjarne Stroustrup: Programming: Principles and Practice Using C++ 2nd Edition
// Chapter 15 Example code
// Japanese Ages Graphing Example

#include "../../Graph.h"
#include "../../Window.h"
#include "../../Simple_window.h"
#include <iostream>
#include <cstdio>
#include <fstream>

struct Distribution
{
	int year, young, middle, old;
};

class Scale
	// data value to coordinate conversion
{
	int cbase;		// coordinate base
	int vbase;		// base of values
	double scale;
public:
	Scale(const int b, const int vb, const double s) : cbase{ b }, vbase{ vb }, scale{ s } { }
	int operator()(int v) const { return cbase + (v - vbase) * scale; }
};

std::istream &operator>>(std::istream &is, Distribution &d);

int main()
{
	using namespace std;
	using namespace Graph_lib;

	try
	{
		constexpr int xmax = 600;	// window size
		constexpr int ymax = 400;

		constexpr int xoffset = 100;	// distance from left-hand side of window to y axis
		constexpr int yoffset = 60;		// distance from bottom of window to x axis

		constexpr int xspace = 40;		// space beyond axis
		constexpr int yspace = 40;

		constexpr int xlength = xmax - xoffset - xspace;	// length of axes
		constexpr int ylength = ymax - yoffset - yspace;

		constexpr int base_year = 1960;
		constexpr int end_year = 2040;

		constexpr double xscale = double(xlength) / (end_year - base_year);
		constexpr double yscale = double(ylength) / 100;

		Scale xs{ xoffset, base_year, xscale };
		Scale ys{ ymax - yoffset, 0, -yscale };

		Graph_lib::Window win{ Point{100, 100}, xmax, ymax, "Aging Japan" };

		Axis x{ Axis::x, Point{xoffset, ymax - yoffset}, xlength,
			(end_year - base_year) / 10, "year      1960      1970      1980      1990      "
			                             "2000      2010      2020      2030      2040      " };
		x.label.move(-100, 0);
		x.set_color(Color::black);

		Axis y{ Axis::y, Point{xoffset, ymax - yoffset}, ylength, 10, "% of population" };
		y.set_color(Color::black);

		Line current_year{ Point{xs(2008), ys(0)}, Point{xs(2008), ys(100)} };
		current_year.set_style(Line_style::dash);
		current_year.set_color(Color::black);

		string file_name = "japanese-age-data.txt.txt";
		ifstream ifs{ file_name };
		if (!ifs)
		{
			error(strerror(errno));
		}

		Open_polyline children;
		Open_polyline adults;
		Open_polyline aged;

		for (Distribution d; ifs >> d;)
		{
			if (d.year < base_year || d.year > end_year)
			{
				error("year out of range");
			}
			if (d.young + d.middle + d.old != 100)
			{
				error("percentages don't add up");
			}
			const int x = xs(d.year);
			children.add(Point{ x, ys(d.young) });
			adults.add(Point{ x, ys(d.middle) });
			aged.add(Point{ x, ys(d.old) });
		}

		Text children_label{ Point{20, children.point(0).y}, "age 0-14" };
		children.set_color(Color::red);
		children_label.set_color(Color::red);

		Text adults_label{ Point{20, adults.point(0).y}, "age 15-64" };
		adults.set_color(Color::blue);
		adults_label.set_color(Color::blue);

		Text aged_label{ Point{20, aged.point(0).y}, "age 65+" };
		aged.set_color(Color::dark_green);
		aged_label.set_color(Color::dark_green);

		win.attach(children);
		win.attach(adults);
		win.attach(aged);

		win.attach(children_label);
		win.attach(adults_label);
		win.attach(aged_label);

		win.attach(x);
		win.attach(y);
		win.attach(current_year);

		gui_main();
	}
	catch (const runtime_error &rte)
	{
		constexpr int xmax = 600;	// window size
		constexpr int ymax = 400;
		Graph_lib::Window win{ Point{ 100, 100 }, xmax, ymax, "Aging Japan" };

		Text err_msg_start{ Point{(xmax / 2) - 100, (ymax / 2)}, "Runtime error: " };
		std::size_t err_msg_start_length = err_msg_start.label().length();
		Text err_msg{ Point{(xmax / 2) + err_msg_start_length - 10, (ymax / 2)}, rte.what() };

		err_msg_start.set_color(Color::black);
		err_msg.set_color(Color::black);

		win.attach(err_msg_start);
		win.attach(err_msg);

		gui_main();
	}
}

std::istream &operator>>(std::istream &is, Distribution &d)
	// assume format: (year: young middle old)
{
	char ch1 = 0;
	char ch2 = 0;
	char ch3 = 0;
	Distribution dd;

	if (is >> ch1 >> dd.year >> ch2 >> dd.young >> dd.middle >> dd.old >> ch3)
	{
		if (ch1 != '(' || ch2 != ':' || ch3 != ')')
		{
			is.clear(std::ios_base::failbit);
			return is;
		}
	}
	else
	{
		return is;
	}
	d = dd;
	return is;
}


And here's the output (notice the length of the red, green and blue lines on the graph; they're shorter than they need to be. The problem might be in line 96. When I tried to do const int x = xs{d.year}; it gave me a compiler error, even though that's how Dr. Stroustrup did it in the book. I used VS2017's Clang/C2 compiler, by the way, with the GNU dialect C++14 compiler flag enabled): https://1drv.ms/i/s!As6LkLqTe7Ps-EPh9Y6Gwh-aDQ9B .
Last edited on
The problem might be in line 96. When I tried to do const int x = xs{d.year}; it gave me a compiler error, even though that's how Dr. Stroustrup did it in the book.

What exactly was the compiler error?

By the way the version of the book I have has int x = xs(d.year); which makes more sense since Distribution doesn't seem to have a constructor with one argument.

I was able to confirm in the Command Prompt that the file is really called "japanese-age-data.txt.txt".

You should look into disabling this "feature", I don't really remember how to disable this "helpful" "feature" but it can be disabled. Perhaps some Window expert can help, but you should be able to find out how by searching the internet for something like "how to show file extensions in windows".

Last edited on
You should look into disabling this "feature", I don't really remember how to disable this "helpful" "feature" but it can be disabled. Perhaps some Window expert can help, but you should be able to find out how by searching the internet for something like "how to show file extensions in windows".

In Windows Explorer, hit the Alt key to display menus, and then select Tools --> Folder Options.

In the dialog that pops up, select the View tab.

In the Advanced Options checklist, unselect the Hide extensions for known file types option.

I also always select Show hidden files, folders, and drives too, but then I'm confident enough with Windows to know not to dick around with the wrong files.
I also have the "Show hidden files" feature enabled. I like it that way.

Well, I could just rename it so that the ".txt.txt" part is gone and then try.

As for errors generated by the compiler for const int x = xs{d.year};, they are as follows (note this is not the MSVC V141 compiler but the Clang with Microsoft Code Gen one):

1>main.cpp(96,14): error : no viable conversion from 'Scale' to 'const int'
1> const int x = xs{ d.year };
1> ^ ~~
1>main.cpp(96,20): error : expected ';' at end of declaration
1> const int x = xs{ d.year };
1> ^
1> ;

The indicators given came out wrong here; the first one should be indicating the variables x and xs, and the second should be pointing at the opening curly brace right next to xs.

Anyway, if the reason that the graphs are smaller than they should be not as a result of anything on 96, then what's the problem?

What version of the book do you have? All I know about mine, though, is that it's the second edition. I have no way to check the exact version and errata number since I downloaded it as a .pdf.

Edit: I think I might know the reason why my graphs came out so short: my input file only has three sets of ages. Look here:


(1960 : 30 64 6)
(1970 : 24 69 7)
(1980 : 23 68 9)
Last edited on
Topic archived. No new replies allowed.