一、问题描述

Personal Diary是一个命令行程序,包含以下四个程序

pdadd
pdlist []
pdshow
pdremove

1.1 pdadd程序

向日记文件dairy.txt中添加一个实体,包含日记的时间、内容

  • 时间:如果日记的时间与dairy.txt中的已有日记重复,则会将原来的日记替换掉

  • 内容:从stdin中逐行读取日记内容,直到单独的一个'.'/EOF截止

1.2 pdlist程序

按照日期的顺序,列出diary.txt中的日记实体

  • 输入:如果通过命令行参数,输入了开始日期和结束日期,则只输出在这两个日期之间的日记
  • 输出:stdout

1.3 pdshow程序

按照输入的日期,列出diary.txt中的对应日记实体的内容

  • 输入:stdin
  • 输出:stdout

1.4 pdremove程序

按照输入的日期,删除diary.txt中的对应日记

  • 输入:stdin
  • 输出:stdout
  • 返回值:0表示删除成功,1表示删除失败

二、实现思路

2.1 定义的类

2.1.1 Date类

成员变量

int year;  //日记的年份
int month; //日记的月份
int day; //日记的日子

成员函数

bool input(); 
//从键盘中输入日期,并且给出输入提示
//当输入的日期为-1/-1/-1时,返回false
//否则返回true
bool input(ifstream& fin);
//从文件流中输入日期
//当输入的日期为-1/-1/-1时,表示整个文件的日记读取完成,返回false
//否则返回true

void output();
//向屏幕中输出日期
void output(ofstream& fout);
//向文件流中输出日期

重载符号

  1. 以年、月、日为第一、二、三关键字进行比较
  2. 注意,-1/-1/-1默认为最大值

2.1.2 Diary类

成员变量

Date date;				//记录日记的日期
vector<string>content; //记录日记的内容

成员函数

bool input(); 
//从键盘中输入日记的日期及内容,并且给出输入提示
//当输入的日期为-1/-1/-1时,返回false
//否则返回true
bool input(ifstream& fin);
//从文件中输入日记的日期及内容
//当输入的日期为-1/-1/-1时,表示整个文件的日记读取完成,返回false
//否则返回true

void output();
//向屏幕中输出日记的日期及内容
void output(ofstream& fout);
//向文件流中输出日记的日期及内容
void outputDate();
//向屏幕中输出日记的日期
Date GetDate();
//获取当前日记的日期

重载符号

  1. date为唯一关键字进行比较

2.2 存储

  1. 日记文件为./code/diary.txt
  2. 按照日期,升序排列所有日记文件
  3. -1/-1/-1为文件结尾

2.3 pdadd

用一个vector< Diary >diary保存已有的所有日记

  1. 首先,从键盘中输入新增日记的信息,放到vector数组里面,记为diary[0]
  2. 然后,从diary.txt文件读取所有日记,放到vector数组里面
  3. diary[1]开始输出存下的所有日记,分为三种情况
    1. diary[i] = diary[0]:根据题意,舍弃diary[i],将diary[0]输入到文件中
    2. diary[i] > diary[0] && diary[i] < diary[0]:由于根据日期升序排列,此时要先输出diary[0],后输出diary[i]
    3. 其它情况:直接输出diary[i]

2.4 pdlist

  1. pdadd类似,从diary.txt中读取所有日记,保存到diary数组中
  2. diary[0]开始输出存下的所有日记,由于只需要表示日记实体,因此我们只输出日记的日期
  3. 至于输出一定范围内的日记,我们只需要在输出的时候,判断一下diary[i]的日期是否在指定的范围内即可
  4. 当没有日记输出时,会输出提示信息

2.5 pdshow

  1. pdlist类似,从diary.txt中读取所有日记,保存到diary数组中
  2. diary[0]开始遍历存下的所有日记,如果日期与输入的相同,则输出当前日记的所有内容
  3. 当没有日记输出时,会输出提示信息

2.6 pdremove

  1. pdadd类似,从diary.txt中读取所有日记,保存到diary数组中
  2. diary[0]开始输出存下的所有日记,分为两种情况
    1. diary[i]的日期与给定的日期相同:跳过该条日记
    2. 否则将diary[i]输入到文件中
  3. 当待删除的日期不存在时,会输出提示信息

三、源码

3.0 公共类

3.0.1 Date

#pragma once
#include <iostream>
#include <fstream>
#include "Utils.h"

class Date {
public:
Date() : year(0), month(0), day(0) {}
Date(int year, int month, int day) : year(year), month(month), day(day) {}

/*
* @brief 从控制台读取日期
*/
bool input();

/*
* @brief 从文件读取日期
* @param fin 文件输入流
*/
bool input(std::ifstream& fin);

/*
* @brief 输出日期到控制台
*/
void output() const;

/*
* @brief 输出日期到文件
* @param fout 文件输出流
*/
void output(std::ostream& fout) const;

public:
bool operator==(const Date& A) const;
bool operator<(const Date& A) const;
bool operator>(const Date& A) const;

private:
int year, month, day;
};

static const Date DATE_MAX = Date(9999, 12, 31);
static const Date DATE_MIN = Date(0, 1, 1);
#pragma warning(disable:4996)
#include "Date.h"

bool Date::input() {
printf("请输入日记的日期:年 月 日\n");
SetColor(Color::Red);
std::cin >> year >> month >> day;
SetColor(Color::Blue);
if (year == -1)return false;
return true;
}

bool Date::input(std::ifstream& fin) {
if (fin.eof()) return false;
fin >> year >> month >> day;
if (year == -1)return false;
return true;
}

void Date::output() const {
SetColor(Color::Red);
printf("%04d/%02d/%02d\n", year, month, day);
SetColor(Color::Blue);
}

void Date::output(std::ostream& fout) const {
fout << year << " " << month << " " << day << std::endl;
}

bool Date::operator==(const Date& A) const {
return year == A.year && month == A.month && day == A.day;
}

bool Date::operator<(const Date& A) const {
if (year == -1)return false;
if (A.year == -1)return true;

if (year != A.year)return year < A.year;
if (month != A.month)return month < A.month;
return day < A.day;
}

bool Date::operator>(const Date& A) const {
return !(*this < A) && !(*this == A);
}

3.0.2 Diary

#pragma once
#include <vector>
#include "Date.h"

class Diary {
public:
Diary() {}
Diary(const Date& date) : date(date) {}

/*
* @brief 从控制台读取日期
*/
bool input();

/*
* @brief 从文件读取日期
* @param fin 文件输入流
*/
bool input(std::ifstream& fin);

/*
* @brief 输出日期到控制台
*/
void output() const;

/*
* @brief 输出日期到文件
* @param fout 文件输出流
*/
void output(std::ostream& fout) const;

/*
* @brief 输出日期到控制台
*/
void outputDate() const;

public:
Date GetDate() const { return date; }

public:
bool operator==(const Diary& A) const;
bool operator<(const Diary& A) const;
bool operator>(const Diary& A) const;

private:
Date date;
std::vector<std::string> content;
};
#pragma warning(disable:4996)
#include "Diary.h"
using namespace std;

bool Diary::input() {
if (date.input() == false)return false;
printf("请逐行输入日记的内容,以.或EOF结束\n");
string s;
getline(cin, s);
while (1) {
SetColor(Color::Yellow);
printf("%2d:", (int)content.size() + 1);
SetColor(Color::Red);
getline(cin, s);
if (s.empty())break;
if (s[0] == '.')break;
content.push_back(s);
}
content.push_back(".\n");

SetColor(Color::Yellow);
printf("日记输入成功\n");
SetColor(Color::Clear);

return true;
}

bool Diary::input(std::ifstream& fin) {
if (date.input(fin) == false)return false;
string s;
getline(fin, s);
while (1) {
getline(fin, s);
if (s.empty())break;
if (s[0] == '.')break;
content.push_back(s);
}
content.push_back(".");
return true;
}

void Diary::output() const {
date.output();
for (int i = 0; i < content.size() - 1; i++)
cout << content[i] << endl;
cout << endl;
}

void Diary::output(std::ostream& fout) const {
date.output(fout);
for (int i = 0; i < content.size(); i++)
fout << content[i] << endl;
}

void Diary::outputDate() const {
date.output();
}

bool Diary::operator==(const Diary& A) const {
return date == A.date;
}

bool Diary::operator<(const Diary& A) const {
return date < A.date;
}

bool Diary::operator>(const Diary& A) const {
return date > A.date;
}

3.0.3 DiaryManager

#pragma once
#include "Diary.h"
#include <set>

class DiaryManager {
public:
static DiaryManager& GetInstance() {
static DiaryManager instance;
return instance;
}

public:
void InputNewDiary();
void OutputAllDiary(Date L = DATE_MIN, Date R = DATE_MAX);
void RemoveDiary(Date now);

private:
void InputAllDiary();
void StoreDiary();

private:
DiaryManager();
~DiaryManager();

std::set<Diary> diary;
};
#include "DiaryManager.h"
using namespace std;

void DiaryManager::InputNewDiary() {
Diary tmp;
tmp.input();
diary.insert(tmp);
}

void DiaryManager::OutputAllDiary(Date L, Date R) {
auto it1 = diary.lower_bound(Diary(L));
auto it2 = diary.upper_bound(Diary(R));
SetColor(Color::Yellow);
if (it1 == it2) {
cout << "No diary in this period!\n";
}
else {
cout << "Diary is as following:\n";
for (auto it = it1; it != it2; it++) {
it->output();
}
}
SetColor(Color::Clear);
}

void DiaryManager::RemoveDiary(Date now) {
SetColor(Color::Yellow);
if(diary.find(Diary(now)) != diary.end()) {
diary.erase(Diary(now));
cout << "Remove success" << endl;
}
else {
cout << "No diary on this day!" << endl;
}
SetColor(Color::Clear);

}

void DiaryManager::InputAllDiary() {
ifstream fin("diary.txt");
while (true) {
Diary tmp;
if (!tmp.input(fin)) break;
diary.insert(tmp);
}
fin.close();
}

void DiaryManager::StoreDiary() {
ofstream fout("diary.txt", ios::out);
for(auto it = diary.begin(); it != diary.end(); it++) {
it->output(fout);
}
fout.close();
}

DiaryManager::DiaryManager() {
InputAllDiary();
}

DiaryManager::~DiaryManager() {
StoreDiary();
}

3.0.4 Utils

#pragma once

#include <string>
#include <Windows.h>

enum class Color {
Red,
Blue,
Green,
Yellow,
Clear
};

static void SetColor(Color color) {
HANDLE hdl = GetStdHandle(STD_OUTPUT_HANDLE);
if (color == Color::Red)SetConsoleTextAttribute(hdl, FOREGROUND_RED | FOREGROUND_INTENSITY);
else if (color == Color::Blue)SetConsoleTextAttribute(hdl, FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY);
else if (color == Color::Green)SetConsoleTextAttribute(hdl, FOREGROUND_GREEN | FOREGROUND_INTENSITY);
else if (color == Color::Yellow)SetConsoleTextAttribute(hdl, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY);
else if (color == Color::Clear)SetConsoleTextAttribute(hdl, FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY);
}

3.1 pdadd

#pragma warning(disable:4996)
#include "../Diary/DiaryManager.h"
using namespace std;

int main(int argc, char* argv[]) {
SetColor(Color::Blue);
if (argc == 2) {
freopen(argv[1], "r", stdin);
}

DiaryManager::GetInstance().InputNewDiary();
return 0;
}

3.2 pdlist

#pragma warning(disable:4996)
#include "../Diary/DiaryManager.h"
using namespace std;

bool CheckFormat(int n, char* s[]) {
if (n != 3)return false;
int len = strlen(s[1]);
int cnt = 0;
for (int i = 0; i < len; i++) {
if (s[1][i] == '/')cnt++;
else if (s[1][i] > '9' || s[1][i] < '0')return false;
}
if (cnt != 2)return false;

len = strlen(s[2]);
cnt = 0;
for (int i = 0; i < len; i++) {
if (s[2][i] == '/')cnt++;
else if (s[2][i] > '9' || s[2][i] < '0')return false;
}
if (cnt != 2)return false;
return true;
}

int main(int argc, char* argv[]) {
SetColor(Color::Blue);
if (argc == 1) {
DiaryManager::GetInstance().OutputAllDiary();
}
else {
if (!CheckFormat(argc, argv)) {
SetColor(Color::Red);
printf("请输入正确的参数格式:xxxx/xx/xx xxxx/xx/xx\n");
}
else {
int LYear = 0, LMonth = 0, LDay = 0;
int RYear = 0, RMonth = 0, RDay = 0;
sscanf(argv[1], "%d/%d/%d", &LYear, &LMonth, &LDay);
sscanf(argv[2], "%d/%d/%d", &RYear, &RMonth, &RDay);
DiaryManager::GetInstance().OutputAllDiary(Date(LYear, LMonth, LDay), Date(RYear, RMonth, RDay));
}
}

return 0;
}

3.3 pdremove

#pragma warning(disable:4996)
#include "../Diary/DiaryManager.h"
using namespace std;

int main(int argc, char* argv[]) {
SetColor(Color::Blue);
if (argc == 2) {
freopen(argv[1], "r", stdin);
}
Date now;
now.input();
DiaryManager::GetInstance().RemoveDiary(now);
return 0;
}

3.4 pdshow

#pragma warning(disable:4996)
#include "../Diary/DiaryManager.h"
using namespace std;

int main(int argc, char* argv[]) {
SetColor(Color::Blue);
if (argc == 2) {
freopen(argv[1], "r", stdin);
}
Date now;
now.input();
DiaryManager::GetInstance().OutputAllDiary(now, now);
return 0;
}

3.5 sample.bat

pdadd sampleAdd.txt
pdshow sampleShow.txt
pdremove sampleRemove.txt
pdlist
pdlist 2022/4/10 2022/4/11
pdlist 2025/4/10 2025/4/11
pause
call pdadd.exe < sampleAdd.txt
call pdshow.exe < sampleShow.txt
call pdremove.exe < sampleRemove.txt
pdlist
pdlist 2022/4/10 2022/4/11
pdlist 2025/4/10 2025/4/11
pause

3.6 diary.txt

2022 4 10
asd123www
asdqw
.
2022 4 11
asd1asdqweyuitbladf
.
2022 4 13
asd123www
asdqweyuitbladf
.
2022 4 14
123456789
00000000
14568+423
.
2022 4 15
123456789
00000000
14568+423
.