首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C++中的数据校验

C++中的数据校验
EN

Code Review用户
提问于 2023-05-27 18:55:12
回答 2查看 1.2K关注 0票数 7

我正在为我的数据结构类(第一年)编写一份作业(哈希表),作业重点是面向对象操作( OOP )和对哈希表的理解。

限制:

  • 原始指针的使用
  • C++11标准
  • 必须为我的HashTable使用向量(不能使用std::map)

这个任务的目标是制作一个新冠肺炎表,它能够从.csv文件中读取数据并将这些数据填充到表中。除此之外,还应该有允许用户手动输入条目的UI。

这是一个好的(低水平) OOP设计吗?我现在还不熟悉多态性、遗传等等。时尚小贴士总是受欢迎的。

run.cpp

代码语言:javascript
复制
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include "run.h"
#include "CovidDB.h"

#define LOG

/** DOCUMENTATION:
 * @author Jakob Balkovec
 * @file assignment3.cpp [Driver Code]
 * @note Driver code for A3
 * 
 * @brief This assigment focuses on using multiple operations regarding BST like:
 *   - Insertion
 *   - Traversals
 *   - Searching
 *   - Deletion
 * 
 * Those operations were implemented using a ShelterBST class that includes a struct for Pets and
 * A struct for the TreeNodes.  
 */


/** @todo
 * test and assert
 * Clarify what to print
 * Make the output neater
*/


/** @name OPID (opeartion ID)
 * @enum for the switch statement
 * @brief Every operation has a numerical value
 */
enum OPID {
           ID_1 = 1,
           ID_2 = 2,
           ID_3 = 3,
           ID_4 = 4,
           ID_5 = 5,
           ID_6 = 6,
           ID_7 = 7,
           ID_8 = 8
};

/**
 * @brief Displays the menu for user interaction.
 */
void menu() {
  std::cout << "\n*** Welcome to the COVID-19 Data Base ***\n\n"
            << "[1]\tCreate a Hash Table with the WHO file\n"
            << "[2]\tAdd a Data entry\n" //segfault
            << "[3]\tGet a Data entry\n"
            << "[4]\tRemove a Data entry\n" //segfault if no entry
            << "[5]\tDisplay HasH Table\n"
            << "[6]\tLog Data [covid_db.log]\n"
            << "[7]\tLog Memory [valgrind.log]\n"
            << "[8]\tQuit\n";
}

/**
 * @brief Takes a string and returns a boolean indicating the validity of the month.
 * 
 * @param month The input month as a string.
 * @return True if the month is valid, false otherwise.
 */
bool valid_month(std::string month) {
  if(month.length() != 1 and month.length() != 2) {
    return false;
  } else if (std::stoi(month) > 13) {
    return false;
  } else if (std::stoi(month) <  1) {
    return false;
  }
  return true;
}

/**
 * @brief Takes a string and returns a boolean indicating the validity of the day.
 * 
 * @param day The input day as a string.
 * @return True if the day is valid, false otherwise.
 */
bool valid_day(std::string day) {
  if(day.length() != 1 and day.length() != 2) {
    return false;
  } else if (std::stoi(day) > 31) {
    return false;
  } else if (std::stoi(day) <  1) {
    return false;
  }
  return true;
}

/**
 * @brief Takes a string reference and returns a boolean indicating the validity of the year.
 * 
 * @param year The input year as a string reference.
 * @return True if the year is valid, false otherwise.
 */
bool valid_year(std::string &year) {
    if(year.length() == 4) {
      year = year[2] + year [3];
      return true;
    } else if(year.length() != 2) {
      return false;
    }
    return true;
}

/**
 * @brief Takes a string reference as an argument and returns a string.
 *
 * @param date The input date as a string reference.
 * @return The processed date as a string.
 * IMPORTANT: @invariant user does not enter a word input
 */

//{
  /** @bug FIX: future dates weren't getting rejected */
//}
std::string get_date(std::string &date) {
  std::string month = "\0";
  std::string day = "\0";
  std::string year = "\0";

  bool is_valid = false;

  while (!is_valid) {
    std::cout << "[FORMAT: mm/dd/yy][Enter a Date]: ";
    std::getline(std::cin, date);
    std::size_t month_loc = date.find("/");
    std::string month_prev = date.substr(0, month_loc);

    if (month_prev[0] == '0') {
      month = month_prev[1]; // if preceding 0 -> trim
    } else {
      month = month_prev; // if single digit -> keep
    }

    std::string s_str = date.substr(month_loc + 1);
    std::size_t day_loc = s_str.find("/");
    std::string day_prev = s_str.substr(0, day_loc);

    if (day_prev[0] == '0') {
      day = day_prev[1];
    } else {
      day = day_prev;
    }

    year = s_str.substr(day_loc + 1);
    is_valid = valid_day(day) && valid_month(month) && valid_year(year);

    //{
    /** @brief
     * c_ stands for current
     * e_ satnds for entered
    */
    //}
    if (is_valid) {
      try {
        std::time_t now = std::time(nullptr);
        std::tm* c_time = std::localtime(&now);

        int c_day = c_time->tm_mday;
        int c_month = c_time->tm_mon + 1; // Month is zero-based
        int c_year = c_time->tm_year % 100; // Last two digits of the year

        const int e_day = std::stoi(day);
        const int e_month = std::stoi(month);
        const int e_year = std::stoi(year);

        if (e_year > c_year) {
          is_valid = false; // Date is in the future
          throw std::invalid_argument("\n[Invalid]\n");
        } else if (e_year == c_year) {
          if (e_month > c_month) {
            is_valid = false; // Date is in the future
            throw std::invalid_argument("\n[Invalid]\n");
          } else if (e_month == c_month) {
            if (e_day > c_day) {
              is_valid = false; // Date is in the future
              throw std::invalid_argument("\n[Invalid]\n");
            }
          }
        } else {
          return month + "/" + day + "/" + year;
        }
      } catch (const std::exception& error) {
        std::cout << error.what();
        is_valid = false;
      }
    }
  }
  return month + "/" + day + "/" + year;
}

/**
 * @brief Takes a string reference as an argument and returns a string.
 *
 * @param country The input country as a string reference.
 * @return The processed country as a string.
 */
std::string get_country(std::string& country) {
  while(true) {
    std::cout << "[Enter a country]: ";
    std::cin >> country;
    std::cin.ignore();

    try {
      //{
        //Using lambda expression to check is the input is not numeric
      //}
      if (!std::all_of(country.begin(), country.end(), [](char c){ return std::isalpha(c); })) {
        throw std::invalid_argument("[Input must be a string]\n");
      }
      std::atomic is_all_lowercase(false);  
      is_all_lowercase.store(std::all_of(country.begin(), country.end(), [](char c){ return std::islower(c); }));

      if (is_all_lowercase) {
        country[0] = std::toupper(country[0]);
        std::transform(country.begin()+1, country.end(), country.begin()+1, [](char c){ return std::tolower(c); });
      } else  {
        return country;
      }
    } catch (const std::exception& e) {
      std::cerr << "\nError: " << e.what() << '\n';
      country = ""; // Reset the input
    }
  }
}

/**
 * @brief Takes an integer reference as an argument and returns an integer.
 *
 * @param cases The input Covid cases as an integer reference.
 * @return The processed Covid cases as an integer.
 */
int get_covid_cases(int &cases) {
  typedef std::numeric_limits IntLimits;
  
  const int INT_MAX = IntLimits::max();
  
  std::cout << "[Enter cases]: ";
  std::cin >> cases;
  
  try {
    if(cases < 0) {
      throw std::invalid_argument("[Cases cannot be negative!]");
    }
    if(cases > INT_MAX) {
        throw std::invalid_argument("[Integer overflow!]");
    } else {
      return cases;
    }
    
  } catch (const std::exception& e) {
    std::cerr << "\nError: " << e.what() << std::endl;
    return 0;
  }
  return 0;
}

/**
 * @brief Takes an integer reference as an argument and returns an integer.
 *
 * @param deaths The input Covid deaths as an integer reference.
 * @return The processed Covid deaths as an integer.
 */
int get_covid_deaths(int &deaths) {
  typedef std::numeric_limits IntLimits;
  
  const int INT_MAX = IntLimits::max();
  
  while(true) {
    std::cout << "[Enter deaths]: ";
    std::cin >> deaths;
    try {
      if(deaths < 0) {
        throw std::invalid_argument("[Cases cannot be negative!]");
      }
      if(deaths > INT_MAX) {
        throw std::invalid_argument("[Integer overflow!]");
      } else {
        return deaths;
      }
    } catch (const std::exception& e) {
      std::cerr << "\nError: " << e.what() << std::endl;
      return 0;
    }
  }
  return 0;
}

/**
 * @brief Prompts the user to enter a valid intiger coresponding to one of the valus in the menu
 *        the user is prompted to enter the input again if it's not a number
 *
 * @return The processed input as an integer.
 */
int get_input() {
  int const MIN = 1;
  int const MAX = 8;
  int choice = 0;
  std::cout << "\n[Enter]: ";
  
  while (true) {
    try {
      std::cin >> choice;
      if (std::cin.fail()) { //std::cin.fail() if the input is not an intiger returns true
        /// @link https://cplusplus.com/forum/beginner/2957/
        
        std::cin.clear(); // clear error flags
        std::cin.ignore(10000, '\n'); // ignore up to 10000 characters or until a newline is encountered
        throw std::invalid_argument("[Invalid input]");
      }
      else if (choice < MIN || choice > MAX) {
        throw std::out_of_range("[Input out of range. Please enter an integer between 1 and 8]");
      }
      else {
        return choice;
      }
    }
    catch (const std::exception& error) {
      std::cout << error.what() << std::endl;
      std::cout << "[Re-enter]: ";
    }
  }
}

/** @name goodbye()
 * @brief The function prompts the user goodbye
 * @remark Handles UI
 * @return void-type
 */
void goodbye() {
  std::cout << "\n\nGoodbye!\n\n";
}

/**
 * @brief Takes a DataEntry* pointer and several arguments (country, date, cases, deaths)
 *        to set the data.
 * 
 * @param data A pointer to a DataEntry object.
 * @param country The country associated with the data.
 * @param date The date associated with the data.
 * @param cases The number of Covid cases associated with the data.
 * @param deaths The number of Covid deaths associated with the data.
 */
void set_data(DataEntry* data, std::string country, std::string date, int cases, int deaths) {
  data->set_country(get_country(country));
  data->set_date(get_date(date));
  data->set_c_cases(get_covid_cases(cases));
  data->set_c_deaths(get_covid_deaths(deaths));
}

/**
 * @brief Executes the main logic of the program in a while(true) loop.
 */
void run() {
  /** DECLARATIONS: */
  std::cout << std::endl << std::endl << std::flush;
  CovidDB data_base;
  DataEntry *data = new DataEntry; //#2 allocation (new DataEntry)
  
  std::string country = "\n";
  std::string date = "\0";
  int cases = 0;
  int deaths = 0;
  /** DECLARATIONS: */
  
  while(true) {
    menu();
    switch(get_input()) {
    case OPID::ID_1: {
      data_base.add_covid_data(COVID_FILE);
      break;
    }
      
    case OPID::ID_2: {
      set_data(data, country, date, cases, deaths);
      bool added = data_base.add(data);
      if(added) {
        std::cout << "\n[Record added]\n";
      } else {
        std::cout << "\n[Failed to add the entry]\n";
      }
      break;
    }
      
    case OPID::ID_3: {
      data_base.fetch_data(data, get_country(country));
      break;
    }
      
    case OPID::ID_4: {
      data_base.remove(get_country(country));
      break;
    }
      
    case OPID::ID_5: {
      data_base.display_table();
      break;
    }
      
    case OPID::ID_6: {
      #ifdef LOG
      data_base.log();
      #else
      std::cout << "\n[Define [LOG macro in run.cpp] to run]\n";
      #endif // LOG
      break;
    }

    case OPID::ID_7: {
      #ifdef LOG
      data_base.log_memory();
      std::exit(EXIT_SUCCESS);
      #else
      std::cout << "\n[Define [LOG macro in run.cpp] to run]\n";
      #endif
      break;
    }

    case OPID::ID_8: {
      goodbye();
      //delete data; //
      std::exit(EXIT_SUCCESS);
      break;
    }

    default: {
      /** @note do nothing...*/
    }
    }
  }
  std::cout << std::endl << std::endl << std::flush;
}

run.h

代码语言:javascript
复制
#include 
#include "CovidDB.h"

/** DOCUMENTATION:
 * @author Jakob Balkovec
 * @file run.cpp [Source Code]
 * @note Header file for helper functions
 * @remark this file defines a set of helper functions that most get or check parameters 
 */

#pragma once

/** @brief 
 * @name get_date: Takes a string reference as an argument and returns a string 
 * @name get_country: Takes a string reference as an argument and returns a string.
 * @name get_country: Takes a string reference as an argument and returns a string.
 * @name get_coivd_cases: Takes an integer reference as an argument and returns an integer.
 * @name get_covid_deaths: Takes an integer reference as an argument and returns an integer.
 */
std::string get_date(std::string &date);
std::string get_country(std::string &country);
int get_coivd_cases(int &cases);
int get_covid_deaths(int &deaths);

/** @brief 
 * @name set_data: Takes a DataEntry* pointer and several arguments
 * @param (country, date, cases, deaths) 
 * to set the data.
 */
void set_data(DataEntry* data, std::string country, std::string date, int cases, int deaths);

/** @brief 
 * @name get_input: Takes no arguments and returns an integer.
 * @name goodbye: Takes no arguments and returns void.
 * @name menu: Takes no arguments and returns void.
 * @name run: Takes no arguments and returns void.
*/
int get_input();
void goodbye();
void menu();
void run();

/** @brief 
 * @name valid_month: Takes a string and returns a boolean indicating the validity of the month.
 * @name valid_day: Takes a string and returns a boolean indicating the validity of the day.
 * @name valid_year: Takes a string reference and returns a boolean indicating the validity of */
bool valid_month(std::string);
bool valid_day(std::string);
bool valid_year(std::string &year);

/**
 * @brief 
 * @name show_last_compiled
 * @param file Name of the file that is being compiled
 * @param date Date of when the file was last compiled
 * @param time Time of when the file was last compiled
 * @param author Author of the code
 * @note all params are arrays of chars
 */
inline void show_last_compiled(const char* file, const char* date, const char* time, const char* author) {
  std::cout << "\n[" << file << "] " << "Last compiled on [" << date << "] at [" << time << "] by [" << author << "]\n" << std::endl;
}

main.cpp

代码语言:javascript
复制
#include "CovidDB.h"
#include "run.h"

#define AUTHOR "Jakob" //define a macro

/**
 * @brief The entry point of the program.
 * @return [EXIT_SUCESS], the exit status of the program.
 */
int main() {
  show_last_compiled(__FILE__, __DATE__, __TIME__, AUTHOR);
  run();
  return EXIT_SUCCESS;
}

MakeFile

代码语言:javascript
复制
CC = g++
CFLAGS = -Wall -Werror -std=c++11 -pedantic -O3

SRCS = run.cpp CovidDB.cpp main.cpp
OBJS = $(SRCS:.cpp=.o)

TARGET = main

all: $(TARGET)

$(TARGET): $(OBJS)
    $(CC) $(CFLAGS) $(OBJS) -o $(TARGET)

%.o: %.cpp
    $(CC) $(CFLAGS) -c $< -o $@

clean:
    rm -f $(OBJS) $(TARGET)

CovidDB.h

代码语言:javascript
复制
#include 
#include 
#include 

#pragma once

static std::string const COVID_FILE = "WHO-COVID-data.csv";

/** DOCUMENTATION:
 * @author Jakob Balkovec
 * @file CovidDB.h [Header file]
 * @note Header file for DataEntry and CovidDB class
 * 
 * @brief This assigment focuses on using multiple operations regarding HasHTables such as:
 *   - Insertion
 *   - Printing
 *   - Hashing
 *   - Deletion
 *   - [File reading]
 * 
 * Those operations were implemented using a DataEntry and a CovidDB class
 */


/**
 * @class DataEntry
 * @brief DataEntry class represents a single entry of COVID-19 data 
 *        for a particular date and country.
 * @note This class is immutable once created.
 */
class DataEntry final {
private:
  std::string date;
  std::string country;
  int c_cases;
  int c_deaths;
  
public:
  DataEntry();
  
  /** @note mutators and acessors*/
  
  inline void set_date(std::string set_date) { this->date = set_date;};
  inline void set_country(std::string set_country) { this->country = set_country;};
  inline void set_c_deaths(int set_c_deaths) { this->c_deaths = set_c_deaths;};
  inline void set_c_cases(int set_c_cases) { this->c_cases = set_c_cases;};
  
  inline int get_c_cases() { return c_cases;};
  inline int get_c_deaths() { return c_deaths;};
  
  inline std::string get_date() { return date;};
  inline std::string get_country() { return country;};   
};

/**
 * @brief A hash table implementation to store Covid-19 data by country
 * @class CovidDB
 * @note The hash table size is fixed at 17.
 */
class CovidDB final {
private:
  int size = 17;
  std::vector> HashTable;
  
public:
  inline CovidDB() {
    HashTable.resize(size);
  }
  
  inline void clear() {
    for (auto& row : HashTable) {
      for (auto& entry : row) {
        delete entry;
      }
      row.clear();
    }
    HashTable.clear();    
    HashTable.shrink_to_fit();
    ///@link https://stackoverflow.com/questions/12587774/destructor-not-being-called
  }

  inline ~CovidDB() { //handles memory
    clear();
    std::cout << "\nDESTROYED\n";
  }
  
  /** @note Copy constructor */
  CovidDB(const CovidDB& other) {
    size = other.size;
    HashTable.resize(size);
    for (int i = 0; i < size; ++i) {
      for (const auto& entry : other.HashTable[i]) {
        HashTable[i].push_back(new DataEntry(*entry));
      }
    }
  }

  /** @note Move constructor */
  CovidDB(CovidDB&& other) noexcept { //noexcept is necessray -> doesn't throw errors
    size = other.size;
    HashTable = std::move(other.HashTable);
    other.size = 0;
  }

  /** @note Overloaded assigment operator*/
  CovidDB& operator=(const CovidDB& other) {
  if (this == &other) {
    return *this;  // Self-assignment, no work needed
  }
  
  // clear the data and resources
  for (auto& row : HashTable) {
    for (auto& entry : row) {
      delete entry;
    }
    row.clear();
  }
  HashTable.clear();
  HashTable.shrink_to_fit();

  // copy the data from the other object
  size = other.size;
  HashTable.resize(size);
  for (int i = 0; i < size; ++i) {
    for (const auto& entry : other.HashTable[i]) {
      HashTable[i].push_back(new DataEntry(*entry));
    }
  }
  return *this;
}

  DataEntry* get(std::string country);
  void fetch_data(DataEntry* set, std::string country);
  bool add(DataEntry* entry);
  void add_covid_data(std::string const COVID_FILE);
  int hash(std::string country);
  void remove(std::string country);
  void display_table(); 
  void log();
  void log_memory();
}; 

CovidDB.h

代码语言:javascript
复制
/** DOCUMENTATION:
 * @author Jakob Balkovec
 * @file CovidDB.cpp [Driver Code]
 * @note Driver code for A4
 * 
 * @brief This assigment focuses on using multiple operations regarding HasHTables such as:
 *   - Insertion
 *   - Printing
 *   - Hashing
 *   - Deletion
 *   - [File reading]
 * 
 * Those operations were implemented using a DataEntry and a CovidDB class
 */

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include "CovidDB.h"

#define LOG
//#define WAIT

/**
 * @brief Constructs an object of DataEntry type with default parameters
 * @return DataEntry Object
 */
DataEntry::DataEntry() {
  this->date = "\0";
  this->country = "\0";
  this->c_cases = 0;
  this->c_deaths = 0;
}

/**
 * @brief Hashfunction that creates a hash
 * @param country std::string entry -> country in the CSV file
 * @return Hash
 */
int CovidDB::hash(std::string country) {
  int sum = 0;
  int count = 0;
  
  for (char c : country) {
    sum = sum + ((count + 1) * c);
    count++;
  }
  return sum % size; //returns the hash
}

/**
 * @brief Inserts one data entry into the hash table. 
 * @param entry The DataEntry object to be added
 * @return true if record is added, false if record is rejected (date < than current date)
 */
bool CovidDB::add(DataEntry* entry) {
  time_t now = time(0);
  tm* ltm = localtime(&now);
  // DATE FORMAT: mm/dd/yy
  std::string current_date_str = std::to_string(1 + ltm->tm_mon) + "/" + std::to_string(ltm->tm_mday) + "/" + std::to_string(ltm->tm_year % 100);
  std::istringstream iss(current_date_str);
  std::tm current_date = {};
  iss >> std::get_time(¤t_date, "%m/%d/%y");
  
  std::tm entry_date = {};
  std::istringstream iss2(entry -> get_date());
  iss2 >> std::get_time(&entry_date, "%m/%d/%y");
  
  if (mktime(¤t_date) > mktime(&entry_date)) {
    std::cout << "[Record rejected]" << std::endl;   
    return false;
  }
  
  int index = hash(entry -> get_country());
  assert(index < 17 and index >= 0);
  
  if (HashTable[index].empty()) {
    HashTable[index].push_back((entry));
  } else {
    bool added = false;
    for (DataEntry* existing_entry : HashTable[index]) {

      std::atomic valid(false);
      valid.store(hash(existing_entry->get_country()) == hash(entry->get_country()) &&
                       existing_entry->get_country() == entry->get_country());
      if (valid) {
        existing_entry->set_date(entry -> get_date());
        existing_entry->set_c_cases(existing_entry->get_c_cases() + entry->get_c_cases());
        existing_entry->set_c_deaths(existing_entry->get_c_deaths() + entry->get_c_deaths());
        added = true;
        delete entry;
        break;
      }
    }
    if (!added) {
      HashTable[index].push_back(entry);
    }
  }
  return true;
}

/**
 * @brief Retrieves a data entry with the specific country name
 * @param country The country to search for
 * @return The DataEntry object with the matching country name, or NULL if no such entry exists
 */
DataEntry* CovidDB::get(std::string country) {
  int index = hash(country);
  assert(index < 17);
  
  for (DataEntry* entry : HashTable[index]) {
    if (entry->get_country() == country) {
      return entry;
    }
  }
  return nullptr;
}

/**
 * @brief Fetches the data entry for a specific country and assigns it
 *        to the provided DataEntry pointer.
 * 
 * @param set A pointer to a DataEntry object where the fetched data will be assigned.
 * @param country The name of the country to fetch data for.
 * @return void
 */
void CovidDB::fetch_data(DataEntry* set, std::string country) {
  set = get(country);
  if(set == nullptr) {
    std::cout << "\n[No entry found for: \"" << country << "\"]\n";
    return; //if nullptr don't derefernece
  }
  char const SPACE = ' ';
  std::cout << std::flush;
  std::cout << "\n[Date: " << set -> get_date() << "]," << SPACE
            << "[Country: " << set -> get_country() << "]," << SPACE
            << "[Cases: " << set -> get_c_cases() << "]," < get_c_deaths() << "]" << SPACE
            << std::endl;
            std::this_thread::sleep_for(std::chrono::milliseconds(100));
}

/**
 * @brief Removes the data entry with the specific country name
 * @param country The country to remove
 */
void CovidDB::remove(std::string country) {
  int index = hash(country);
  
  for (auto it = HashTable[index].begin(); it != HashTable[index].end(); ++it) {
    if ((*it)->get_country() == country) {
      delete *it;
      HashTable[index].erase(it);
      return;
    }
  }
  std::cout << "\n[No entry found for: " << country << "]" << std::endl;
}

/**
 * @brief Prints the contents of the hash table using
 *        nested for each loops
*/
//{
// @bug when adding 2 entires with the same hash -> SIGSEV 
//}
void CovidDB::display_table() {
  char const STICK = '|';
  bool is_empty = true;

  /** @note guard against printing an empty table*/
  for(const auto& vec : HashTable) { //if 1D is empty
    if(!vec.empty()) {
      is_empty = false;
      break;
    }
  }

  if(is_empty) {
    std::cout << "\n[Data Base is empty]\n";
    return;
  }
  /** @note guard against printing an empty table*/
  
  std::cout << "\n[Printing Data Base]\n|\n";
  for (int i = 0; i < 3; ++i) {
    std::this_thread::sleep_for(std::chrono::milliseconds(200));
    std::cout << STICK << std::endl;
  }
  std::cout << std::flush; //flush buffer
  std::string const SPACE = " ";
  
  for(std::vector vec : HashTable) {
    for(DataEntry* entry : vec) {
      if (entry != nullptr) { //guard against dereferencing nullptr
        std::cout << std::flush;
        std::cout << "[Date: " << entry -> get_date() << "]," << SPACE
                  << "[Country: " << entry -> get_country() << "]," << SPACE
                  << "[Cases: " << entry -> get_c_cases() << "]," << SPACE
                  << "[Deaths: " << entry -> get_c_deaths() << "]"
                  << std::endl;
                  #ifdef WAIT
                  std::this_thread::sleep_for(std::chrono::milliseconds(100));
                  #endif //WAIT
      }
    }
  }
  std::cout << std::endl;
  return;
}

/**
 * @brief Logs the contents of the hash table using
 *        nested for each loops and writes them to a .log file
 */
void CovidDB::log() {
  #ifdef LOG
  add_covid_data(COVID_FILE); //add data and log
  std::ofstream covid_file;
  covid_file.open("covid_db.log");

  if (covid_file.is_open()) {
    covid_file << std::flush;

    std::string const SPACE = " ";
    covid_file << "\n\n****************************** COIVD DATA LOG ******************************\n\n\n";
    for (auto& vec : HashTable) {
      for (auto& entry : vec) {
        if (entry != nullptr) {
          covid_file << "[Date: " << entry->get_date() << "]," << SPACE
                     << "[Country: " << entry->get_country() << "]," << SPACE
                     << "[Cases: " << entry->get_c_cases() << "]," << SPACE
                     << "[Deaths: " << entry->get_c_deaths() << "]"
                     << std::endl;
        }
      }
    }
  covid_file.close();
  } else {
    covid_file << "\n[Error opening the file covidDB.log]\n";
    std::exit(EXIT_FAILURE);
  }
  std::this_thread::sleep_for(std::chrono::milliseconds(500));
  std::cout << "\n------ [Log avalible] ------\n\n";
  std::this_thread::sleep_for(std::chrono::milliseconds(500));
  std::exit(EXIT_SUCCESS);
  return;
}
#else
std::cout << "\n[Define [LOG macro in CovidDB.cpp] to run]\n";
#endif // LOG

/**
 * @brief Reads a CSV file containing COVID data and adds the data to the database.
 * @param COVID_FILE The name of the CSV file to read.
 * @return void
 */
void CovidDB::add_covid_data(std::string const COVID_FILE) {
  std::ifstream file(COVID_FILE);

  /** @note measure timne it takes to fetch and process data*/
  std::chrono::steady_clock::time_point startTime;
  std::chrono::steady_clock::time_point endTime;

  startTime = std::chrono::steady_clock::now(); //start stopwatch
  
  if (!file) {
    std::cout << "\n[File ERROR]\n " << COVID_FILE << std::endl;
    std::exit(EXIT_FAILURE);
  }
  std::cout << std::flush;
  std::cout << "\n[Fetching Data]\n";
  std::cout << std::flush;
  std::string line;
  std::getline(file, line); // skip header line
  
  std::string latest_date_str = "11/02/22"; // initialize to an old date
  std::tm latest_date = {};
  std::istringstream iss(latest_date_str);
  iss >> std::get_time(&latest_date, "%m/%d/%y");
  
  while (std::getline(file, line)) {
    std::stringstream ss(line);
    std::string country, date_str, cases_str, deaths_str;
    std::getline(ss, date_str, ',');
    std::getline(ss, country, ',');
    std::getline(ss, cases_str, ',');
    std::getline(ss, deaths_str, ',');
    
    int cases = std::stoi(cases_str);
    int deaths = std::stoi(deaths_str);
    
    std::tm entry_date = {};
    std::istringstream iss2(date_str);
    iss2 >> std::get_time(&entry_date, "%m/%d/%y");
    
    if (mktime(&entry_date) > mktime(&latest_date)) {
      latest_date_str = date_str;
      latest_date = entry_date;
    } 
    
    DataEntry* entry = new DataEntry(); //#1 allocation
    entry->set_country(country);
    entry->set_date(latest_date_str);
    entry->set_c_cases(cases);
    entry->set_c_deaths(deaths);
    
    add(entry);
  }
  file.close();

  endTime = std::chrono::steady_clock::now(); //stop stopwatch

  std::chrono::duration elapsedSeconds = endTime - startTime;

  /** @note static cast it to an int and round up*/
  auto elapsedSecondsCount = static_cast(std::round(elapsedSeconds.count()));

  std::cout << std::flush; //flush isotream buffer
  std::cout << "|\n|\n|\n[Data Fetched] [Elapsed Time: " << elapsedSecondsCount << "s]\n" << std::flush;
  return;
} 

/**
 * @brief logs memory by running valgrind
 */
void CovidDB::log_memory() {
  #ifdef LOG
  std::cout << "\n[NOTE]: Please quit the program right after it re-runs with \"Valgrind\"\n";
  std::this_thread::sleep_for(std::chrono::seconds(4));
  std::string command = "valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes --num-callers=20 -s --log-file=valgrind.log ./main";
  std::system(command.c_str());
}
#else
std::cout << "\n[Define [LOG macro in CovidDB.cpp] to run]\n";
#endif
```#qcStackCode#
代码语言:javascript
复制
EN

回答 2

Code Review用户

回答已采纳

发布于 2023-05-27 20:24:12

票数 8
EN

Code Review用户

发布于 2023-05-27 19:45:28

要专注于你问到的具体事情:

您可以使用>运算符进行比较。您可以比较两个std::chrono::year_month_day对象或两个std::chrono::time_point对象。无论哪种方式,您都可以使用std::chrono::from_streamcinstd::chrono::utc_clock::now (在夏时制时不会有奇怪的bug)读取时间,以获得当前时间,并将两者转换成一种通用格式,使您可以比较if (input_time > current_time)

目前,您正在返回string值,然后检查一组单独的函数,如valid_day。通过使用异常,您可以使代码更短、更甜:如果算法的任何步骤失败,立即将throw作为异常(可能是std::runtime_error),并将其返回到输入循环中。这个catch块可以打印它收到的错误消息。然后,您的函数只有在它们成功时才会正常返回,所以以后不需要大量的if语句来检查错误--如果出现了错误,控制就会返回到catch块。

如果您需要处理无效的结果,而不仅仅是短路,那么将所有可能失败的东西都返回std::optional是一种很好的方法。与传统的返回魔术数字的方法相比,它更难解决问题,并且比返回成功/错误代码和将结果设置为副作用更容易编写和链接计算步骤。

编辑

在补充代码中,有两件事跳出了我的视野:从ID_1 = 1ID_8 = 8的八个值的D19完全没有意义。更重要的是,不要将函数体放在.h文件中,因为这些函数体将被复制粘贴到多个源文件中。这将导致编译失败,因为链接器将在多个对象文件中找到相同函数的冲突定义。(例外是inline/constexpr/consteval函数,这些函数可以安全地放入标头中,但是应该保持简短和甜蜜。这些函数在被调用的任何地方都会被扩展,而不是变成对只应该编译一次的函数的调用,因此它们不需要有一个和唯一的一个定义。)

票数 7
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/285194

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档