reading a structured file

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

[code language=”cpp”]
// 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();
}
[/code]

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