我正在编写一个关于从给定日期查找一周中的某一天的小项目。我创建了一个菜单,有 3 个选项(按数字选择): 1. 功能 1、2. 功能 2、0. 退出程序。 菜单将如下所示:
#include <iomanip>
#include <iostream>
#include <string.h>
#include <Windows.h>
#include <conio.h> //dung de sdung getch(), text color
#include <bits/stdc++.h>
#include <vector>
#include <fstream> //doc file
using namespace std;
vector<pair<int, string> > days{{0,"SATURDAY"}, {1, "SUNDAY"}, {2,"MONDAY"}, {3,"TUESDAY"},
{4,"WEDNESDAY"}, {5,"THURSDAY"}, {6,"FRIDAY"}};
bool validDate(int d, int m, int y, char c) //Check input
{
if(c == '/')
{
if(y >= 1 && y <= 2999)
{
if(m >= 1 && m <= 12)
{
if(d >= 1 && d <= 31)
{
if( (d >= 1 && d <= 30) && (m == 4|| m == 6|| m == 9|| m == 11))
return true;
else if((d >= 1 && d <= 30) && (m == 1 || m == 3 || m == 5 || m == 7 || m == 8|| m == 10 ||m == 12))
return true;
else if((d >= 1 && d <= 28) && (m == 2))
return true;
else if(d == 29 && m == 2 && ((y % 400 == 0)||(y % 4 == 0 && y % 100 != 0))) //leapyear
return true;
else
return false;
}
else return false;
}
else return false;
}
else return false;
}
else return false;
}
/*
The following code is Zeller congruence method to calculate the day of given date
*/
int ZellerCongruence(int givenday, int givenmonth, int givenyear)
{
if(givenmonth == 1) //In Zeller congruence, Jan & Feb is 13th and 14th month in the previous year
{
givenmonth = 13;
givenyear--;
}
if(givenmonth == 2)
{
givenmonth = 14;
givenyear--;
}
int q = givenday;
int m = givenmonth;
int k = givenyear % 100;
int j = givenyear / 100;
int h = q + (13 * (m + 1) / 5) + k + (k / 4) + (j / 4) + (5 * j);
h = h % 7;
return h;
}
void SubMenu1()
{
cout << endl << "FIND THE DAY OF THE WEEK FROM THE GIVEN DATE" << endl;
cout << endl << "Notice: Program can only calculate the year from 1 to 2999" << endl;
cout << endl << "Enter date in dd/mm/yyyy format and press enter: ";
int givenday, givenmonth, givenyear;
char c;
cin >> givenday;
cin >> c;
cin >> givenmonth;
cin >> c;
cin >> givenyear;
if(validDate(givenday, givenmonth, givenyear, c) == false)
{
cout << "Invalid Date \n";
Sleep(200);
return;
}
int h = ZellerCongruence(givenday, givenmonth, givenyear);
cout << endl << "The given day of date is: " << days[h].second << endl;
}
void SubMenu2()
{
cout << "FIND THE DAY OF THE WEEK AFTER N DAYS FROM THE GIVEN DATE" << endl;
cout << endl << "Notice: Program can only calculate the year from 1 to 2999" << endl;
cout << endl << "Enter date in dd/mm/yyyy format and press enter: ";
int givenday, givenmonth, givenyear;
char c;
cin >> givenday;
cin >> c;
cin >> givenmonth;
cin >> c;
cin >> givenyear;
if(validDate(givenday, givenmonth, givenyear, c) == false)
{
cout << "Invalid Date \n";
Sleep(200);
return;
}
int h = ZellerCongruence(givenday, givenmonth, givenyear);
int n;
cout << "Enter the value of n: ";
cin >> n;
if(n <= 0)
{
cout << "Invalid value, n must be a positive interger." << endl;
Sleep(200);
return;
}
h = h + n;
if(h < 7)
{
cout << "The day of the week after " << n << " days from the given date is: " << days[h].second << endl;
}
else
{
h = h % 7;
cout << "The day of the week after " << n << " days from the given date is: " << days[h].second << endl;
}
}
void Menu()
{
cout << endl << "MAIN MENU" << endl;
cout << "1. Find the day of the week from the given date" << endl;
cout << "2. Find the day of the week after n days from the given date" << endl;
cout << "0. Quit the program" << endl;
}
int main()
{
int choice;
start:
system("CLS");
Menu();
cout<<"Enter Your Choice: ";
s:
char c = getch(); // getch() method pauses the Output Console until a key is pressed
if(c >= '0' && c <= '2')
{
choice = c - 48; // translate the char values to the int values (cause '0' == 48 in ASCII). We can also write: choice = c - '0'
cout << c;
goto s2;
}
else
{
choice = getch();
}
goto s;
s2:
Sleep(200);
system("CLS");
switch(choice)
{
case 0:
cout << "See you again !" << endl;
return 0;
break;
case 1:
SubMenu1();
system("pause");
system("CLS");
goto start;
break;
case 2:
SubMenu2();
system("pause");
system("CLS");
goto start;
break;
default:
cout << "Invalid Number..."<<endl;
system("pause");
system("CLS");
goto start;
break;
}
return 0;
}
我想问是否可以改进选择方式,从按数字改为使用箭头键。此外,我希望每次箭头键移动时,该处的文本行都会改变颜色。你能帮我吗?
正如评论已经说过的,更改终端输出颜色取决于平台,并且可能因您使用的每个终端而异。由于您使用 Windows,我认为我的解决方案也应该适用于您的计算机。
您可以通过多种方式更改窗口中的终端文本颜色,例如
system("color 0A")
是最常见的方式之一。但是,不建议这样做,并且您只有一小部分可能的颜色(背景和前景各 16 种)。
这就是为什么我更喜欢使用转义序列(请参阅这篇文章)将文本颜色更改为某个 RGB 值,如以下函数所示:
void setColor(std::uint8_t r, std::uint8_t g, std::uint8_t b) {
// https://learn.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences
// print an escape sequence which lets us control the text color in the windows terminal with RGB values
std::string output{ "\x1b[38;2;" };
output += std::to_string(r) + ";";
output += std::to_string(g) + ";";
output += std::to_string(b) + "m";
std::cout << output;
}
要注册是否按下了某些箭头键,您可以使用例如来自 Windows API 的
GetKeyState()
,但我更喜欢使用一些普通的文本输入函数。由于您已经使用了非标准 <conio.h>
标头,我也这样做了,因为我认为以这种方式实现它非常容易。从 here 我获得了 arrowKeyIndicator
的值以及按下箭头键时从 _getch()
(getch()
已弃用)返回的键的值。
我的函数等待用户输入(正在阻塞)并返回用户按下的箭头键。当用户按下 ENTER 键(或者准确地说是
'\r'
)时,我的函数按设计返回无效键,但您可以更改它以满足您的需要。
enum Key : unsigned char {
None = 0, ArrowUp = 72, ArrowDown = 80, ArrowLeft = 75, ArrowRight = 77
};
Key getArrowKey() {
// https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797
constexpr unsigned char arrowKeyIndicator{ 224 };
unsigned char ch{};
// loop until the user presses either an arrow key (which will get us two chars)
// or the user presses enter
while ((ch = static_cast<char>(_getch())) != arrowKeyIndicator) {
// return when the user presses enter (so there is a way to exit the loop without having to click an arrow key)
if (ch == '\r') {
return Key::None;
}
}
// the first char was some character indicating arrow keys which is the same for all of them
// now we get the second char
ch = static_cast<char>(_getch());
// and we return the corresponding arrow key
for (const Key k : { Key::ArrowUp, Key::ArrowDown, Key::ArrowLeft, Key::ArrowRight }) {
if (ch == k) {
return k;
}
}
// if it was not a arrow key, return `Key::None` (or could be another value to mark failure to get an arrow key)
return Key::None;
}
如果您想测试这两个功能,您可以使用以下代码片段来完成。请注意 print 语句末尾的
'\r'
(您当然可以将其移动到字符串文字中),它允许您写入同一行,而不是 '\n'
它将转到下一行。因此,您可以更改颜色并再次打印文本。但请注意,重写比以前更短的输出将使旧行中的一些字符仍然可见,因此您要么需要使用不同的方法,要么只是确保在使用 '\r'
时不要打印更小的行。
int main() {
Key k{};
do {
std::cout << "When you press an arrow key, the color changes. If you press ENTER, the program terminates." << '\r';
k = getArrowKey();
switch (k)
{
case Key::ArrowUp:
setColor(0xFF, 0x00, 0x00);
break;
case Key::ArrowDown:
setColor(0x00, 0xFF, 0x00);
break;
case Key::ArrowLeft:
setColor(0x00, 0x00, 0xFF);
break;
case Key::ArrowRight:
setColor(0xFF, 0xA5, 0x00);
break;
default:
break;
}
} while (k != Key::None);
return 0;
}