2. fields.push_back(field);
}
return fields;
}
CountryData parseCountryData(const string& line) {
vector fields = split(line, ',');
if (fields.size() != 12) {
throw runtime_error("invalid line: " + line);
}
string name = fields[0];
double pop1980 = fieldToDouble(fields[1]);
double pop2010 = fieldToDouble(fields[11]);
return CountryData(name, pop1980, pop2010);
}
vector readCountryData(const string& filename) {
vector countryDataList;
ifstream infile(filename);
if (!infile) {
throw runtime_error("unable to open file: " + filename);
}
string line;
while (getline(infile, line)) {
try {
CountryData countryData = parseCountryData(line);
countryDataList.push_back(countryData);
}
catch (const runtime_error&) {
// ignore the error; just don't add this one to the vector
}
}
return countryDataList;
}
vector ReadCountryData(const string & filename)
{
vector data;
ifstream ifs{ filename };
3. string line;
// Read the header line
getline(ifs, line);
while (getline(ifs, line)) {
try {
CountryData cd = ParseCountryData(line);
data.push_back(cd);
}
catch (runtime_error & e) {
string error{ e.what() };
}
}
return data;
}
CountrtyData.h
#pragma once
#include
#include
class CountryData
{
public:
CountryData(const std::string & n, double p1, double p2);
std::string GetName() const;
double CalcGrowth() const;
private:
std::string name;
double pop1980;
double pop2010;
};
CountryData ParseCountryData(const std::string& line);
std::vector ReadCountryData(const std::string& filename);
utilities.h
#pragma once
4. #include
#include
// Split a string into sub-strings based on the given
// delimiter. For example, if the input is
//
// Ron,Ginny,George,Fred,Percy,Bill,Charlie
//
// the output is a 7-element vector with
// the following entries:
//
// [0] = "Ron"
// [1] = "Ginny"
// [2] = "George"
// [3] = "Fred"
// [4] = "Percy"
// [5] = "Bill"
// [6] = "Charlie"
//
std::vector split(const std::string& s, char seperator);
// Converts a database field string to an integer.
// If the field cannot be converted to an int,
// a runtime_error exception is thrown.
int fieldToInt(const std::string & field);
// Converts a database field to a double.
// If the field cannot be converted to an double,
// a runtime_error exception is thrown.
double fieldToDouble(const std::string & field);
utilities.cpp
#include
#include
#include "utilities.h"
std::vector split(const std::string & s, char sep)
{
std::vector fields;
size_t i = 0;
5. size_t len = s.length();
while (i < len) {
// Skip characters until we find a separator.
size_t j = i;
while ((i < len) && (s[i] != sep))
i++;
// Add this field - which may be empty! - to the output.
if (j == i)
fields.emplace_back();
else
fields.emplace_back(&s[j], i - j);
// Skip past the separator.
i++;
// If this put us at the end of the string,
// we need to account for the blank field at
// the end.
if (i == len)
fields.emplace_back();
}
return fields;
}
int fieldToInt(const std::string & field)
{
if (field.length() == 0)
throw std::runtime_error("Cannot convert 0-length string");
if (field == "--" || (field == "NA"))
throw std::runtime_error("No data provided");
return strtol(field.c_str(), NULL, 10);
}
double fieldToDouble(const std::string & field)
{
if (field.length() == 0)
throw std::runtime_error("Cannot convert 0-length string");
if (field == "--" || (field == "NA"))
throw std::runtime_error("No data provided");
return strtod(field.c_str(), NULL);
6. }
CountryDataTest.cpp
#include
#include
#include
#include "CountryData.h"
bool CompareGrowth(const CountryData& lhs, const CountryData& rhs)
{
return lhs.CalcGrowth() < rhs.CalcGrowth();
}
int main()
{
std::vector data = ReadCountryData("population_by_country.csv");
if (data.size() == 0)
{
std::cout << "No data read!n";
return 1;
}
auto iters = minmax_element(std::begin(data), std::end(data), CompareGrowth);
auto min_iter = iters.first;
auto max_iter = iters.second;
std::cout << max_iter->GetName()
<< " had the highest growth at "
<< max_iter->CalcGrowth()
<< " million.n";
std::cout << min_iter->GetName()
<< " had the lowest growth at "
<< min_iter->CalcGrowth()
<< " million.n";
std::string country;
while (std::getline(std::cin, country))
{
auto iter = std::find_if(std::begin(data), std::end(data),
[&country](const CountryData & c)
{
7. return c.GetName() == country;
});
if (iter == std::end(data))
std::cout << "There is no data for " << country << 'n';
else
std::cout << "The population of " << country << " increased by "
<< std::fixed << std::setprecision(2)
<< iter->CalcGrowth() << " million from 1980 to 2010.n";
}
}
REVISION NOTE 4/17/2020: Since are covering some material in a different order than
originally planned, more of this lab was already done for you. Every file but CountryData.cpp is
read-only, so you only need to do the TODOs in that file. The function ReadcountryData is
actually already complete. In this lab, you will implement a very simple class called CountryData
that stores some population data for a country, and (more importantly) write functions to: - parse
a line of text and convert it to a countryData object - read a file line by line and return a vector
containing all the valid data in the file The CountryData class contains three data members: - The
name of the country. - The population of the country in 1980 - The population of the country in
2010 This data will be read from a file called population_by_country.csv, each line of which
looks like this: Canada,
24.5933,24.9,25.2019,25.4563,25.7018,,32.65668,32.93596,33.2127,33.48721,33.75974 (I've
elided some data to make it fit on one line.) The data consists of fields separated by commas -
thus it's referred to as a "comma-separated values" file. (Spreadsheets can read these directly - try
it.) The first field is the name of the country, and the others are the population values in millions
from 1980 to 2010. I've provided a header file containing the declaration of the countryData
class; all you need to do is to fill in the details. The class itself should be easy; all you need to do
is: - Finish the constructor - Implement getName(), which simply returns the country name -
Implement calcGrowth0, which calculates the population growth from 1980-2010 Each of these
can actually be done in one line, though taking more than one line may help readability. The hard
part will be the two functions that actually read the data. The first is parsecountryData (), whose
signature is: CountryData parseCountryData(const std: :string & line); The line argument is one
line of the file. The function needs to split the data into separate fields (using a split () function
that I'll provide), and then return a CountryData object. The county name is the first field, the
8. 1980 population is the second, and the 2010 population is the last. The population data will need
to be converted from std: : string to double. I've provided a function called fieldToDouble() that
recommend you use rather than writing your own. The fieldToDouble() function that I provide
will throw an exception for invalid floating-point data. The exception should NOT be caught in
parsecountryData(); see below. The second function is readCountryData(), whose signature is:
The second function is readCountryData(), whose signature is: std: : vector readCountryData
(const std: string a filename); This function needs to open the file as an ifstream, and use
getline() to read the file line by line. It should call parseCountryData() to convert this line of data
to a countryData object, and store each valid line in a vector. This vector is the return value of
the function. The call to parsecountryData( ) should be wrapped in a try block. Recall that the
syntax is: try { / call parsecountryData() // add this CountryData to the vector } catch (std:
runtime_error &e e { // ignore the error; just don't add this one to the vector In case we have not
covered exceptions by the time you do this lab, this code is provided for you. Provided functions
I've provided utilities.h and utilities. cpp which declare/define these functions: double
fieldToDouble (const std: string & field); fieldToDouble() converts the given std: : string to a
floating point value, which is returned as a double. It does some basic error checking, and throws
an exception if the field cannot be converted. std: :vector split(const std: :string & s, char sep);
split () separates the given std: : string into individual fields based on the given separator
character. The fields are returned as a vector. I've also provided Title_ratings.cpp from a
different project. You can use that as a guide for writing your parseCountryData() function.