/ Published in: C++
This code creates a program to read in incremental payment data from a comma-separated text file, to accumulate the data and to output the results to a different comma-separated text file.
EXAMPLE INPUT DATA
A short input file might contain the following:
Product, Origin Year, Development Year, Incremental Value
Comp, 1992, 1992, 110.0
Comp, 1992, 1993, 170.0
Comp, 1993, 1993, 200.0
Non-Comp, 1990, 1990, 45.2
Non-Comp, 1990, 1991, 64.8
Non-Comp, 1990, 1993, 37.0
Non-Comp, 1991, 1991, 50.0
Non-Comp, 1991, 1992, 75.0
Non-Comp, 1991, 1993, 25.0
Non-Comp, 1992, 1992, 55.0
Non-Comp, 1992, 1993, 85.0
Non-Comp, 1993, 1993, 100.0
EXAMPLE INPUT DATA
A short input file might contain the following:
Product, Origin Year, Development Year, Incremental Value
Comp, 1992, 1992, 110.0
Comp, 1992, 1993, 170.0
Comp, 1993, 1993, 200.0
Non-Comp, 1990, 1990, 45.2
Non-Comp, 1990, 1991, 64.8
Non-Comp, 1990, 1993, 37.0
Non-Comp, 1991, 1991, 50.0
Non-Comp, 1991, 1992, 75.0
Non-Comp, 1991, 1993, 25.0
Non-Comp, 1992, 1992, 55.0
Non-Comp, 1992, 1993, 85.0
Non-Comp, 1993, 1993, 100.0
Expand |
Embed | Plain Text
Copy this code and paste it in your HTML
//PRODUCT.HPP FILE #pragma once #include <iostream> #include <vector> #include <boost/fusion/include/vector.hpp> typedef boost::fusion::vector4<std::string, unsigned int, unsigned int, double> bfVector4; class Product { public: Product(const bfVector4& line); void findMinAndMaxOriginYears(); void addData(const bfVector4& currentBFVector); const std::string getProductName() const { return m_productName; } const unsigned int getMinOriginYear() const { return m_minOriginYear; } void setMinOriginYear(unsigned int minOriginYear) { m_minOriginYear = minOriginYear; } void setMaxOriginYear(unsigned int maxOriginYear) { m_maxOriginYear = maxOriginYear; } const unsigned int getMaxOriginYear() const { return m_maxOriginYear; } const std::vector<bfVector4> getData() const { return m_data; } private: std::string m_productName; unsigned int m_minOriginYear; unsigned int m_maxOriginYear; std::vector<bfVector4> m_data; }; //END PRODUCT.HPP FILE //PRODUCT.CPP FILE #include "Product.hpp" #include <boost/fusion/sequence.hpp> #include <boost/foreach.hpp> Product::Product(const bfVector4& line) { m_productName = boost::fusion::at_c<0>(line); m_data.push_back(line); m_minOriginYear = std::numeric_limits<unsigned int>::max(); m_maxOriginYear = std::numeric_limits<unsigned int>::min(); } void Product::findMinAndMaxOriginYears() { BOOST_FOREACH(const bfVector4& line, m_data) { unsigned int currentOriginYear = boost::fusion::at_c<1>(line); if (currentOriginYear < m_minOriginYear) { m_minOriginYear = currentOriginYear; continue; } if (currentOriginYear > m_maxOriginYear) { m_maxOriginYear = currentOriginYear; continue; } } } void Product::addData(const bfVector4& currentBFVector) { m_data.push_back(currentBFVector); } //END PRODUCT.CPP FILE //MAIN.CPP FILE #define _SCL_SECURE_NO_WARNINGS #include <iostream> #include <fstream> #include <vector> #include <map> #include <set> #include <boost/filesystem.hpp> #include <boost/algorithm/string.hpp> #include <boost/fusion/sequence.hpp> #include <boost/lexical_cast.hpp> #include <boost/foreach.hpp> #include "Product.hpp" typedef boost::fusion::vector3<std::string, unsigned int, unsigned int> bfVector3; void readFile(const std::string& inputFile, std::set<std::string>& productNames, std::vector<Product>& products) { std::ifstream inputFileStream(inputFile); std::string currentLine; std::map<std::string, unsigned int> headerPositions; //the order I want the headers to be in headerPositions.insert(std::make_pair("Product", 0)); headerPositions.insert(std::make_pair("OriginYear", 1)); headerPositions.insert(std::make_pair("DevelopmentYear", 2)); headerPositions.insert(std::make_pair("IncrementalValue", 3)); //process first line first - the headers line, to determine what column goes where; reordering the columns in memory to my liking std::map<unsigned int, int> correctHeaderPositions; std::getline(inputFileStream, currentLine); currentLine.erase(std::remove_if(currentLine.begin(), currentLine.end(), isspace), currentLine.end()); std::vector<std::string> headers; boost::split(headers, currentLine, boost::is_any_of(",")); if (headers.size() != 4) std::cout << "There should only be 4 headers in your text file. Any extra incorrect columns will be ignored." << std::endl; for (int i = 0; i < (int)headers.size(); ++i) { if (!headerPositions.count(headers[i])) { correctHeaderPositions.insert(std::make_pair(i, -(i + 1))); std::cout << "The header \'" << headers[i] << "\' is incorrect. This column will be ignored." << std::endl; continue; } correctHeaderPositions.insert(std::make_pair(i, headerPositions[headers[i]])); } //now read all the other lines while (std::getline(inputFileStream, currentLine)) { bool skip = false; std::vector<std::string> currentData; bfVector4 currentBFVector; currentLine.erase(std::remove_if(currentLine.begin(), currentLine.end(), isspace), currentLine.end()); boost::split(currentData, currentLine, boost::is_any_of(",")); for (unsigned int i = 0; i < currentData.size(); ++i) { if (correctHeaderPositions[i] < 0 || skip) continue; unsigned int realPosition = correctHeaderPositions[i]; int currentOriginYear = 0; int currentDevelopmentYear = 0; double currentIncrementalValue = 0.0; switch (realPosition) { case 0: try { boost::fusion::at_c<0>(currentBFVector) = boost::lexical_cast<std::string>(currentData[correctHeaderPositions[0]]); } catch (boost::bad_lexical_cast) { std::cout << "The Product name of the line: " << std::endl << currentLine << std::endl << "is not a valid string. Discarding line." << std::endl; skip = true; break; } break; case 1: try { currentOriginYear = boost::lexical_cast<int>(currentData[correctHeaderPositions[1]]); } catch (boost::bad_lexical_cast) { std::cout << "The Origin Year of the line:" << std::endl << currentLine << std::endl << "is not a valid integer. Discarding line." << std::endl; skip = true; break; } //check if the current origin year is positive and non-zero if (currentOriginYear <= 0) { std::cout << "The Origin Year of the line:" << std::endl << currentLine << std::endl << "is zero or negative. Discarding line." << std::endl; skip = true; break; } boost::fusion::at_c<1>(currentBFVector) = currentOriginYear; break; case 2: try { currentDevelopmentYear = boost::lexical_cast<unsigned int>(currentData[correctHeaderPositions[2]]); } catch (boost::bad_lexical_cast) { std::cout << "The Development Year of the line:" << std::endl << currentLine << std::endl << "is not a valid integer. Discarding line." << std::endl; skip = true; break; } //check if the current development year is positive and non-zero if (currentDevelopmentYear <= 0) { std::cout << "The Development Year of the line:" << std::endl << currentLine << std::endl << "is zero or negative. Discarding line." << std::endl; skip = true; break; } //check if the current development year is less than it's respective origin year if (currentDevelopmentYear < (int)boost::fusion::at_c<1>(currentBFVector)) { std::cout << "The Development Year of the line:" << std::endl << currentLine << std::endl << "is less than it's corresponding origin year. " << "Discarding line." << std::endl; skip = true; break; } boost::fusion::at_c<2>(currentBFVector) = currentDevelopmentYear; break; case 3: try { currentIncrementalValue = boost::lexical_cast<double>(currentData[correctHeaderPositions[3]]); } catch (boost::bad_lexical_cast) { std::cout << "The Incremental Value of the line:" << std::endl << currentLine << std::endl << "is not a valid real number. " << "Discarding line." << std::endl; skip = true; break; } //check if the current incremental value is positive and non-zero if (currentIncrementalValue <= 0.0) { std::cout << "The Incremental Value of the line:" << std::endl << currentLine << std::endl << "zero or negative. Discarding line." << std::endl; skip = true; break; } boost::fusion::at_c<3>(currentBFVector) = currentIncrementalValue; break; } } //if this is a new product, add it to the products vector if (!productNames.count(boost::fusion::at_c<0>(currentBFVector))) { products.push_back(Product(currentBFVector)); productNames.insert(boost::fusion::at_c<0>(currentBFVector)); continue; } //if this is an existing product, then add the data to the relevant product for (unsigned int i = 0; i < products.size(); ++i) if (products[i].getProductName() == boost::fusion::at_c<0>(currentBFVector)) products[i].addData(currentBFVector); } } bool findGlobalMinAndMaxOriginYears(std::vector<Product>& products, unsigned int& minOriginYear, unsigned int& maxOriginYear) { BOOST_FOREACH(Product& product, products) { product.findMinAndMaxOriginYears(); unsigned int currentMinOriginYear = product.getMinOriginYear(); unsigned int currentMaxOriginYear = product.getMaxOriginYear(); if (currentMinOriginYear < minOriginYear) minOriginYear = currentMinOriginYear; if (currentMaxOriginYear > maxOriginYear) maxOriginYear = currentMaxOriginYear; } if (minOriginYear < std::numeric_limits<unsigned int>::max() && maxOriginYear > std::numeric_limits<unsigned int>::min()) return true; return false; } void fillInBlanks(std::vector<Product>& products, const unsigned int minOriginYear, const unsigned int maxOriginYear) { BOOST_FOREACH(Product& product, products) { unsigned int productMinOriginYear = product.getMinOriginYear(); unsigned int productMaxOriginYear = product.getMaxOriginYear(); std::string productName = product.getProductName(); //fill in blanks in between min and max product origin years for (unsigned int i = productMinOriginYear; i <= productMaxOriginYear; ++i) { for (unsigned int j = i; j <= productMaxOriginYear; ++j) { bool needsInserting = true; BOOST_FOREACH(const bfVector4& line, product.getData()) { if (bfVector3(boost::fusion::at_c<0>(line), boost::fusion::at_c<1>(line), boost::fusion::at_c<2>(line)) == bfVector3(productName, i, j)) { needsInserting = false; break; } } if (needsInserting) product.addData(bfVector4(productName, i, j, 0.0)); } } //fill in missing origin years before the current product min origin year if (minOriginYear < productMinOriginYear) { for (unsigned int i = minOriginYear; i < productMinOriginYear; ++i) for (unsigned int j = i; j <= maxOriginYear; ++j) product.addData(bfVector4(productName, i, j, 0.0)); product.setMinOriginYear(minOriginYear); } //fill in missing origin years after the current product max origin year if (maxOriginYear > productMaxOriginYear) { for (unsigned int i = productMaxOriginYear + 1; i <= maxOriginYear; ++i) for (unsigned int j = i; j <= maxOriginYear; ++j) product.addData(bfVector4(productName, i, j, 0.0)); product.setMaxOriginYear(maxOriginYear); } } } void outputFile(std::ofstream& output, const std::vector<Product> products, const unsigned int minOriginYear, const unsigned int developmentYears) { output << minOriginYear << ", " << developmentYears << std::endl; BOOST_FOREACH(const Product& product, products) { output << product.getProductName(); std::vector<double> productOutput((developmentYears * (developmentYears + 1)) / 2, 0.0); //vector of incremental values to be summed //go through each origin year and workout where in the productOutput array the corresponding incremental value for each line should go for (unsigned int i = product.getMinOriginYear(); i <= product.getMaxOriginYear(); ++i) { BOOST_FOREACH(bfVector4 line, product.getData()) { if (boost::fusion::at_c<1>(line) != i) continue; unsigned int currentOriginYearFromMin = i - product.getMinOriginYear(); //the following index was worked out using the fact that the number of incremental values decreases by 1 each time each time //the origin year increases, and the difference between the development year and the origin year, amongst other things. //used pen and paper to work out this index int index = (currentOriginYearFromMin * ((2 * (developmentYears - 1)) + 3 - currentOriginYearFromMin)) / 2 + (boost::fusion::at_c<2>(line) - i); productOutput.at(index) = boost::fusion::at_c<3>(line); } } //now go through vector just constructed and sum relevant values for (unsigned int i = developmentYears; i > 0; --i) { for (unsigned int j = 0; j < i; ++j) { double sum = 0.0; for (int k = j; k >= 0; --k) { if (productOutput[k] != 0) sum += productOutput[k]; } output << ", " << sum; } productOutput.erase(productOutput.begin(), productOutput.begin() + i); } output << std::endl; } output.close(); } void main() { boost::filesystem::path inputFile, outputFileDir; std::cout << "Please enter the path of the file you wish to examine: " << std::endl; std::cin >> inputFile; std::cout << "Note: The headers in your text file must be exactly the following, and nothing more:" << std::endl << "Product" << std::endl << "Origin Year" << std::endl << "Development Year" << std::endl << "Incremental Value" << std::endl; //do some checks on the file specified first if (!boost::filesystem::exists(inputFile)) { std::cout << "The file specified doesn't exist." << std::endl; return; } if (inputFile.extension() != ".txt") { std::cout << " The file specified is not a .txt file. Please specify a path to a .txt file." << std::endl; return; } std::cout << "Please enter the path of the directory that you wish the output file to be saved in: " << std::endl; std::cin >> outputFileDir; if (!boost::filesystem::exists(outputFileDir)) boost::filesystem::create_directories(outputFileDir); //start reading file std::set<std::string> productNames; std::vector<Product> products; readFile(inputFile.string(), productNames, products); unsigned int minOriginYear = std::numeric_limits<unsigned int>::max(); unsigned int maxOriginYear = std::numeric_limits<unsigned int>::min(); unsigned int developmentYears = std::numeric_limits<unsigned int>::max(); if (findGlobalMinAndMaxOriginYears(products, minOriginYear, maxOriginYear)) developmentYears = maxOriginYear - minOriginYear + 1; fillInBlanks(products, minOriginYear, maxOriginYear); //output results std::ofstream output((outputFileDir / "TriangleOutput.txt").string().c_str()); outputFile(output, products, minOriginYear, developmentYears); } END OF MAIN.CPP FILE