reading a structured file

Bjarne Stroustrup “Programming Principles and Practice Using C++”
Chapter 10 Exercise 5
Using std_lib_facilities.h by Bjarne Stroustrup.

//	Philipp Siedler
//	Bjarne Stroustrup's PPP
//	Chapter 10 Exercise 5

#include "std_lib_facilities.h"

const int not_a_reading = -7777;
const int not_a_month = -1;

struct Day {
	vector<double> hour{ vector<double>(24,not_a_reading) };
};

struct Month {
	int month{ not_a_month };
	vector<Day> day{ 32 };
};

struct Year {
	int year;
	vector<Month> month{ 12 };
};

struct Reading {
	int day;
	int hour;
	double temperature;
};

istream& operator>>(istream& is, Reading& r) {
	char ch1;
	if (is >> ch1 && ch1 != '(') {
		is.unget();
		is.clear(ios_base::failbit);
		return is;
	}

	char ch2;
	int d;
	int h;
	double t;
	is >> d >> h >> t >> ch2;
	if (!is || ch2 != ')') error("bad reading");
	r.day = d;
	r.hour = h;
	r.temperature = t;
	return is;
}

void end_of_loop(istream& ist, char term, const string& message) {
	if (ist.fail()) {
		ist.clear();
		char ch;
		if (ist >> ch && ch == term) return;
		error(message);
	}
}

constexpr int implausible_min = -200;
constexpr int implausible_max = 200;

bool is_valid(const Reading& r) {
	if (r.day < 1 || 31 < r.day) return false;
	if (r.hour < 0 || 23 < r.hour) return false;
	if (r.temperature < implausible_min || implausible_max < r.temperature) {
		return false;
	}
	return true;
}

vector<string> month_input_tbl = {
	"jan", "feb", "mar", "apr", "may", "jun", "jul",
	"aug", "sep", "oct", "nov", "dec"
};

int month_to_int(string s) {
	for (int i = 0; i < 12; ++i) {
		if (month_input_tbl[i] == s) return i;
	}
}

vector<string> month_print_tbl = {
	"January", "February", "March", "April", "May", "June", "July",
	"August", "September", "October", "November", "December"
};

string int_to_month(int i) {
	if (i < 0 || 12 <= i) error("bad month index");
	return month_print_tbl[i];
}

istream& operator>>(istream& is, Month& m) {
	char ch = 0;
	if (is >> ch && ch != '{') {
		is.unget();
		is.clear(ios_base::failbit);
		return is;
	}

	string month_marker;
	string mm;
	is >> month_marker >> mm;
	if (!is || month_marker != "month") error("bad start of month");
	m.month = month_to_int(mm);

	int duplicates = 0;
	int invalids = 0;
	for (Reading r; is >> r;) {
		if (is_valid(r)) {
			if (m.day[r.day].hour[r.hour] != not_a_reading) {
				++duplicates;
			}
			m.day[r.day].hour[r.hour] = r.temperature;
		}
		else {
			++invalids;
		}
	}
	if (invalids) error("invalid readings in month", invalids);
	if (duplicates) error("duplicate readings in month", duplicates);
	end_of_loop(is, '}', "bad end of month");
	return is;
}

istream& operator>>(istream& is, Year& y) {
	char ch;
	is >> ch;
	if (ch != '{') {
		is.unget();
		is.clear(ios::failbit);
		return is;
	}

	string year_marker;
	int yy;
	is >> year_marker >> yy;
	if (!is || year_marker != "year") error("bad start of year");
	y.year = yy;

	while (true) {
		Month m;
		if (!(is >> m)) break;
		y.month[m.month] = m;
	}

	end_of_loop(is, '}', "bad end of year");
	return is;
}

ostream& operator<<(ostream& os, Year& y) {
	os << y.year;
	return os;
}

void print_years(ostream& ost, Year& y) {
	ost << y << endl;
}

int main()
try
{
	cout << "Please enter input file name\n";
	string iname;
	cin >> iname;

	ifstream ist{ iname };
	if (!ist) error("can't open input file ", iname);

	ist.exceptions(ist.exceptions() | ios_base::badbit);

	cout << "Please enter output file name\n";
	string oname;
	cin >> oname;
	ofstream ost{ oname };
	if (!ost) error("can't open output file", oname);
	
	vector<Year> ys;
	while (true) {
		Year y;
		if (!(ist >> y)) break;
		ys.push_back(y);
	}

	cout << "read " << ys.size() << " years of readings\n";

	for (Year& y : ys) print_years(ost, y);

	keep_window_open();

}
catch (runtime_error e) {
	cout << e.what() << endl;
	keep_window_open();
}
catch (...) {
	cout << "Exiting" << endl;
	keep_window_open();
}
Output:
Please enter input file name
input/input.txt
Please enter output file name
output/output.txt
read 4 years of readings
Please enter a character to exit
Input-File input.txt:
{ year 1990 }

{ year 1991
    { month jun }
}

{ year 1992
    { month jan
        (1 0 61.5)
    }
    { month feb
        (1 1 64)
        (2 2 65.2)
    }
}

{year 2000
    { month feb
        (1 1 68)
        (2 3 66.66 )
        (1 0 67.2)
    }
    { month dec
        (15 15 -9.2)
        (15 14 -8.8)
        (14 0 -2)
    }

}
Output-File output.txt:
1990
1991
1992
2000

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.