https://rentry.org/PPP2_p370

// Code derived from Stroustrup's PPP2 book
// § 10.11.2 Reading structured values
//  -and beginning on p 370

#include <fstream>
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <string>
#include <vector>

using namespace std;

void error(const string s) { throw runtime_error(s); }
void error(const string s1, const string s2) { error(s1 + s2); }
void error(const string& s, int i)
{
  ostringstream os;
  os << s << ": " << i;
  error(os.str());
}

//------------------------------------------------------------------------------

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

// read a temperature reading from is into r
// format: ( 3 4 9.7 )
// check format, but don’t bother with data validity
istream& operator>>(istream& is, Reading& r)
{
  char ch1;
  if (is >> ch1 && ch1 != '(') {  // could it be a Reading?
    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");  // messed-up reading

  r.day         = d;
  r.hour        = h;
  r.temperature = t;

  return is;
}

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

// a rough test
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;
}

//------------------------------------------------------------------------------

const int not_a_reading = -7777;  // less than absolute zero
const int not_a_month   = -1;

// a day of temperature readings, organized by hour
struct Day {
  vector<double> hour{vector<double>(24, not_a_reading)};
};

// a month of temperature readings, organized by day
struct Month {
  int         month{not_a_month};  // [0:11] January is 0
  vector<Day> day{32};             // [1:31] one vector of readings per day
};

// a year of temperature readings, organized by month
struct Year {
  int           year;       // positive == A.D.
  vector<Month> month{12};  // [0:11] January is 0
};

//------------------------------------------------------------------------------

int  month_to_int(string s);
void end_of_loop(istream& ist, char term, const string& message);

//------------------------------------------------------------------------------
// read a month from is into m
// format: { month feb . . . }
istream& operator>>(istream& is, Month& m)
{
  char ch = 0;
  if (is >> ch && ch != '{') {
    is.unget();
    is.clear(ios_base::failbit);  // we failed to read a Month
    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;
}

//------------------------------------------------------------------------------
// read a year from is into y
// format: { year 1972 . . . }
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") {
    if (is.eof())
      return is;  // all data is parsed (eof)
    else
      error("bad start of year");
  }

  y.year = yy;

  while (true) {
    Month m;  // get a clean m each time around
    if (! (is >> m))
      break;

    y.month[m.month] = m;
  }

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

  return is;
}

//------------------------------------------------------------------------------

void end_of_loop(istream& ist, char term, const string& message)
{
  if (ist.fail()) {  // use term as terminator and/or separator
    ist.clear();

    char ch;
    if (ist >> ch && ch == term)
      return;  // all is fine

    error(message);
  }
}

//------------------------------------------------------------------------------

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

// is s the name of a month? If so return its index [0:11] otherwise –1
int month_to_int(string s)
{
  for (int i = 0; i < 12; ++i)
    if (month_input_tbl[i] == s)
      return i;

  return -1;
}

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

// months [0:11]
string int_to_month(int i)
{
  if (i < 0 || 12 <= i)
    error("bad month index");

  return month_print_tbl[i];
}

//------------------------------------------------------------------------------
void print_year(ostream& ost, const Year& y)
{
  ost << "\nYEAR:  " << y.year << '\n';

  for (const auto& m : y.month) {
    if (m.month != not_a_month) {
      ost << "  MONTH:  " << int_to_month(m.month) << '\n';

      for (int day = 1; day < (int)m.day.size(); ++day) {
        for (int hour = 0; hour < (int)m.day[day].hour.size(); ++hour) {
          auto temp = m.day[day].hour[hour];
          if (temp != not_a_reading)
            ost << "    (" << day  //
                << ", " << hour    //
                << ", " << temp    //
                << ")\n";
        }
      }
    }
  }
}

//------------------------------------------------------------------------------

int main()
try {
  // open an input file:
  cout << "Please enter input file name (eg, odd_format.txt ):  ";
  string iname;
  cin >> iname;
  ifstream ist{iname};
  if (! ist)
    error("can't open input file ", iname);

  ist.exceptions(ist.exceptions() | ios_base::badbit);  // throw for bad()

  // open an output file:
  cout << "Please enter output file name:  ";
  string oname;
  cin >> oname;
  ofstream ost{oname};
  if (! ost)
    error("can't open output file ", oname);

  // read an arbitrary number of years from input stream:
  vector<Year> ys;
  while (true) {
    Year y;  // get a freshly initialized Year each time around
    if (! (ist >> y))
      break;

    ys.push_back(y);
  }

  // write formatted data to output stream:
  cout << "read " << ys.size() << " years of readings\n";
  for (Year& y : ys)
    print_year(ost, y);

} catch (exception& e) {
  cerr << "error: " << e.what() << '\n';
  return 1;

} catch (...) {
  cerr << "Oops: unknown exception!\n";
  return 2;
}

build & run:

g++ -std=c++20 -O2 -Wall -pedantic ./ch_10/main_p370.cpp && ./a.out

PrevUpNext
Bonus: version 2

Edit
Pub: 07 Apr 2023 22:38 UTC
Edit: 03 May 2023 10:15 UTC
Views: 529