#include #include #include #include #include static std::string trim(const std::string& str) { std::string result = str; result.erase(result.find_last_not_of(" \t\n\r\f\v") + 1); result.erase(0, result.find_first_not_of(" \t\n\r\f\v")); return result; } static bool isLeapYear(int year) { return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0); } static bool isValidDate(const std::string& date) { if (date.size() != 10) return false; if (date[4] != '-' || date[7] != '-') return false; for (size_t i = 0; i < date.size(); ++i) { if (i == 4 || i == 7) continue; if (!std::isdigit(static_cast(date[i]))) return false; } const int year = (date[0] - '0') * 1000 + (date[1] - '0') * 100 + (date[2] - '0') * 10 + (date[3] - '0'); const int month = (date[5] - '0') * 10 + (date[6] - '0'); const int day = (date[8] - '0') * 10 + (date[9] - '0'); if (year <= 0) return false; if (month < 1 || month > 12) return false; if (day < 1) return false; static const int daysInMonth[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; int maxDay = daysInMonth[month - 1]; if (month == 2 && isLeapYear(year)) maxDay = 29; return day <= maxDay; } static std::map validateCSV(const std::string& fileName) { std::ifstream inputFile(fileName.c_str()); if (!inputFile.is_open() || !inputFile.good()) std::cerr << ("Error: could not open file.") << std::endl; std::string line; bool firstLine = true; static bool input = false; // smelly, pefect std::map data; while (std::getline(inputFile, line)) { std::size_t separator = line.find(','); if (separator == std::string::npos) { separator = line.find('|'); // That is not a CSV file... if (separator == std::string::npos) { std::cerr << "Error: bad input => " + line << std::endl; continue; } } std::string first = trim(line.substr(0, separator)); std::string second = trim(line.substr(separator + 1)); if (second.empty()) { std::cerr << "Error: bad input => " + line << std::endl; continue; } if (firstLine) { firstLine = false; if (first != "date" || (input && second != "value") || (!input && second != "exchange_rate")) { std::cerr << "Error: bad => " + line << std::endl; continue; } continue; } if (!isValidDate(first)) { std::cerr << "Error: bad input => " + first << std::endl; continue; } bool badSecond = false; bool dot = false; bool dash = false; for (size_t i = 0; i < second.size(); ++i) { if (std::isdigit(static_cast(second[i]))) continue; if (!dot && second[i] == '.' && i + 1 < second.size()) { dot = true; continue; } if (!dash && second[i] == '-' && i == 0 && i + 1 < second.size()) { dash = true; continue; } std::cerr << "Error: bad input => " + second << std::endl; badSecond = true; break; } if (badSecond) continue; const double value = std::strtod(second.c_str(), NULL); if (value < 0) { std::cerr << "Error: not a positive number." << std::endl; continue; } else if (input && value > 1000) { std::cerr << "Error: too large a number." << std::endl; continue; } const std::pair pair(first, value); if (!input && data.find(first) != data.end()) { std::cerr << "Error: bad input => " + first << std::endl; continue; } data.insert(pair); } input = true; return data; } int main(int argc, char* argv[]) { if (argc != 2) { std::cout << "Usage: " << argv[0] << " " << std::endl; return EXIT_FAILURE; } try { const std::map data = validateCSV("data.csv"); if (data.empty()) throw std::runtime_error("data file is empty"); const BitcoinExchange exchange(data); const std::map input = validateCSV(argv[1]); for (std::map::const_iterator it = input.begin(); it != input.end(); ++it) { const double value = exchange.getValue(it->first); std::cout << it->first << " => " << it->second << " = " << value * it->second << std::endl; } } catch (const std::exception& exception) { std::cerr << "Exception caught, cause: " << exception.what() << std::endl; return EXIT_FAILURE; } }