C++在重复输出十次后,控制台输出到cmd出现问题

3
我的代码的目的是在控制台上打印出红色、蓝色、绿色或关闭,当输入r、b、g或o时。键盘输入x可终止程序。
一堆空格字符也会输出,并设置背景颜色以在终端中显示一个实心的颜色块。
int i;
for (i=0;i<21;i++) {    // for loop to print a load of empty lines
    cout << "                                             \n";
}   // end of for loop

起初一切都正常,但在第十次更改之后(即按下r、b、g或o,然后按下ENTER),颜色的实心块就会溢出\n字符。

有人能告诉我为什么会这样吗?

我还期望int main()中的else语句会处理任何与预期r、b、g、o或x不匹配的输入。对于单个字符,它可以工作,但如果输入超过一个字符,它就会变得混乱不堪,连续滚动且不停止输入。

为什么会这样呢?

完整代码(使用MinGW g++.exe编译,通过NppExec运行于Win7命令提示符):

#include <windows.h>
#include "iostream"
using namespace std;

/*******FUNCTION PROTOTYPES*********/
void TermClr(int ClrHeight);
void SetColor(int value);
void PrintOut(int output, int col1, int col2);

/*******MAIN PROGRAM****************/
int main() {

    int output = 0; // variable for current colour being output, 0-3 for OFF, RED, BLUE, GREEN
    char inkey[2];  // console input written to the INKEY variable
    PrintOut(0,119,7);  // calls printout function, dark grey background for the light, normal grey for text

    while(1){   //while loops forever until break
        cin.getline(inkey,2);   //waits for input, stored to INKEY
        if(!strcmp(inkey,"x")){     // string compare whether INKEY matches "x"
            cout << "\nProgram Terminated in\n3...";
            Sleep(1000);
            cout << "\n2...";
            Sleep(1000);
            cout << "\n1...";
            Sleep(1000);
            break;  //breaks if 'x' is input
        }   // end if inkey x
        else if(!strcmp(inkey,"o")){
            PrintOut(0,119,7);  // calls PrintOut function, 'output' set to 0, red background for the light, red for text
            continue;
        }   // end of if inkey o
        else if(!strcmp(inkey,"r")){
            PrintOut(1,204,12);
            continue; 
        }   // end of if inkey r
        else if(!strcmp(inkey,"b")){
            PrintOut(2,153,9);
            continue; 
        }   // end of if inkey b
        else if(!strcmp(inkey,"g")){
            PrintOut(3,170,10);
            continue; 
        }   // end of if inkey g
        else{
            TermClr(30);
            printf("Input not recognized\n(x=terminate, o=off, r=red, b=blue, g=green)");
            continue;
        }   // end of else 
    }   //end of while
return 0;
}   // end of main

/*******FUNCTIONS*******************/
// function to clear terminal - ClrHeight is the number of new rows, use enough in the function call to clear the terminal
void TermClr(int ClrHeight) {
    int i;
    for ( i = 0; i < ClrHeight; i++ ) {
    putchar('\n');
    }   // end of for loop
}   // end TermClr

// function for changing terminal font colours (run the exe in cmd prompt to see colours, doesn't work in nppexec)
void SetColor(int value){
    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),  value);
}   // end SetColor

// function to print the required text to terminal
void PrintOut(int output, int col1, int col2) {     // three inputs needed, the 'output' variable 0-3, the light colour text type, and the writing text type
    TermClr(5);         // calls func to clear teminal, 5 rows
    const char *light[4] = {"OFF", "RED", "BLUE", "GREEN"};     // defines the four light colours
    SetColor(col1);
    int i;
    for (i=0;i<21;i++) {    // for loop to print a load of empty lines with the background colour 'col1'
        cout << "                                             \n";
    }   // end of for loop
    SetColor(7);
    cout << "\nColour - ";
    SetColor(col2);         // calls the function to change the console font colour (shows if run in cmd prompt, but not nppexec console)
    cout << light[output];
    SetColor(7);
    cout << " (Output " << output << ")\n(x=terminate, o=off, r=red, b=blue, g=green)";
}   //end PrintOut

编辑后添加:
显示问题的图片
我尝试使用std::endl代替\n,但没有任何区别

进一步编辑:
如果我减少固体颜色的for循环迭代次数,即

int i;
for (i=0;i<3;i++) {    // for loop to print a load of empty lines
    cout << "                                             \n";
}   // end of for loop

通过在每次while循环中打印更少的行,我可以获得更多的更改输出,但在足够多的次数后,输出仍会溢出。编辑3:评论区的马克·兰索姆(Mark Ransom)已经确定了第一个问题的罪魁祸首是cmd窗口。增加缓冲区高度可以允许更多的程序周期而不会出现错误行为。(ALT+SPACE, 属性, 布局选项卡)James Whyte的答案解决了多个键盘输入的问题,尽管我需要做一些工作以使坏输入被忽略而不是被单独解释每个字符。编辑4:如果我使用system("CLS")而不是添加新行来清除屏幕,缓冲区大小就不再重要(猜测每次调用此函数时都会清除)。我知道这是不好的做法,但它起作用了!有没有其他的不是不好的做法?

2
我无法复制这个问题。请提供截图以展示该问题。此外,您是否尝试使用std::endl而不是“\n”? - bcr
有趣。我可以复制这个问题,但我也不理解它。 - user4581301
std::endl和\n在输出时得到相同的结果,附加了屏幕截图以进行说明。 - Cammack
1
我怀疑这与终端窗口的滚动行为有关。您使用的终端窗口大小是多少? - Mark Ransom
@Mark,你说对了 - 在cmd中,ALT+SPACE > 属性,布局选项卡。增加屏幕缓冲区高度(我尝试了1000与默认的300)。现在程序可以在出现问题之前执行更多的循环。谢谢。 - Cammack
2个回答

1

[编辑] !- 至少这是解决两个字符问题无限循环的方法。

问题出现是由于使用char数组而不是单个char,我认为。我将您的代码放入一个新项目中,并复制了您的问题,没有问题(有趣的玩笑),并决定进行一些修补以消除错误。

问题发生在您尝试将行读取到已经有值但未清除的char数组中时。它只是决定跳过它,导致无限循环。

/*******MAIN PROGRAM****************/
int main() {

int output = 0; // variable for current colour being output, 0-3 for OFF, RED, BLUE, GREEN
char inkey;  // console input written to the INKEY variable
PrintOut(0, 119, 7);  // calls printout function, dark grey background for the light, normal grey for text

while (1) {   //while loops forever until break
    cin >> inkey;   //waits for input, stored to INKEY
    if (inkey == 'x') {     // string compare whether INKEY matches "x"
        cout << "\nProgram Terminated in\n3...";
        Sleep(1000);
        cout << "\n2...";
        Sleep(1000);
        cout << "\n1...";
        Sleep(1000);
        break;  //breaks if 'x' is input
    }   // end if inkey x
    else if (inkey == 'o') {
        PrintOut(0, 119, 7);  // calls PrintOut function, 'output' set to 0, red background for the light, red for text
        continue;
    }   // end of if inkey o
    else if (inkey == 'r') {
        PrintOut(1, 204, 12);
        continue;
    }   // end of if inkey r
    else if (inkey == 'b') {
        PrintOut(2, 153, 9);
        continue;
    }   // end of if inkey b
    else if (inkey == 'g') {
        PrintOut(3, 170, 10);
        continue;
    }   // end of if inkey g
    else {
        TermClr(30);
        printf("Input not recognized\n(x=terminate, o=off, r=red, b=blue, g=green)");
        continue;
    }   // end of else 
}   //end of while
return 0;
}   // end of main

最后,如果你只需要在任何时候读取单个字符,请不要使用字符数组和字符串比较。像上面那样比较单个字符,或者更好的方法是使用类似以下内容的 switch case,这样它更易读。
switch (inkey)
{
case 'r': PrintOut(1, 204, 12); continue; // Print red square.
case 'g': PrintOut(2, 153, 9); continue; // Print green square.
case 'b': PrintOut(3, 170, 10); continue; // Print blue square.
case 'o': PrintOut(0, 119, 7); continue; //Print a grey block for inactive.
case 'x':
    cout << "\nProgram Terminated in\n3...";
    Sleep(1000);
    cout << "\n2...";
    Sleep(1000);
    cout << "\n1...";
    Sleep(1000);
    break;  //breaks if 'x' is input
default: 
    TermClr(30);
    printf("Input not recognized\n(x=terminate, o=off, r=red, b=blue, g=green)");
    continue;
} // end switch

1
这太棒了。我最初尝试以与顶部修复程序相同的方式实现,我认为我犯的错误是使用双引号而不是单引号 - 即if (inkey =="x"),而不是所需的if (inkey == 'x')。在其他问题未解决之前,我将暂时搁置标记回答。 - Cammack
1
另一个问题已经被Mark Ransom在问题下的评论中指出 - 问题是cmd窗口缓冲区高度。我已将此答案标记为正确,以解决滚动问题,谢谢James。 - Cammack

0

最终的工作代码如下所示。

在原问题下,Mark Ransom的评论指出了第一个问题,即cmd缓冲区高度。 我使用system("CLS");来清除屏幕解决了这个问题。(我知道这被认为是不好的做法,但我写的任何代码都只是用于个人使用,这是我能让它工作的唯一方法)。

当输入超过一个字符时出现的滚动问题在James Whyte的答案中得到了正确的识别和纠正,然而,它揭示了另一个问题(在我看来真是遗憾,因为我发现switch/case语法更加优雅!)-

如果输入超过一个字符,则每个单独的字符将依次被解释并通过while循环运行。因此,如果我输入“rgb”,显示器会在“r”和“g”输出之间闪烁,然后才会停留在“b”输出上。通过回到原始字符数组和字符串比较解决方案,并添加一个if(inkey.length() == 1)循环,可以捕获除单个字符以外的任何内容,并通知用户输入无效。

最后,我希望在接收到无效输入时,之前显示的输出仍然保留。这是通过为先前的输出添加变量,并将其作为参数调用PrintOut函数来实现的。
代码:
#include <windows.h>
#include "iostream"

using namespace std;

    int outputprev;
    int col1prev;
    int col2prev;

/*******FUNCTION PROTOTYPES*********/
void SetColor(int value);
void PrintOut(int output, int col1, int col2);

/*******MAIN PROGRAM****************/
int main() {

    int output = 0; // variable for current colour being output, 0-3 for OFF, RED, BLUE, GREEN
    string inkey;   // console input written to the INKEY variable
    PrintOut(0, 119, 7);    // calls printout function, dark grey background for the light, normal grey for text

    while(inkey != "x"){   //while loops forever until break
        getline(cin, inkey);   //waits for input, stored to INKEY
        if(inkey.length() == 1) {
            if(inkey.compare("o") == 0){
                PrintOut(0,119,7);  // calls PrintOut function, 'output' set to 0, red background for the light, red for text
                continue;
            }   // end of if inkey o
            else if(inkey.compare("r") == 0){
                PrintOut(1,204,12);
                continue; 
            }   // end of if inkey r
            else if(inkey.compare("b") == 0){
                PrintOut(2,153,9);
                continue; 
            }   // end of if inkey b
            else if(inkey.compare("g") == 0){
                PrintOut(3,170,10);
                continue; 
            }   // end of if inkey g
            else{
                PrintOut(outputprev, col1prev, col2prev); // Print for previous value
                cout << " **INVALID INPUT**";
                continue;   }   // end of else 
        }   // end of if input length
        else {
            PrintOut(outputprev, col1prev, col2prev); // Print for previous value
            cout << " **INVALID INPUT**";
            continue;  // end of else 
        }
    }   //end of while
    cout << "\nProgram Terminating";
    Sleep(1000);
return 0;
}   // end of main

/*******FUNCTIONS*******************/
// function for changing terminal font colours (run the exe in cmd prompt to see colours, doesn't work in nppexec)
void SetColor(int value){
    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),  value);
}   // end SetColor

// function to print the required text to terminal
void PrintOut(int output, int col1, int col2) {     // three inputs needed, the 'output' variable 0-3, the light colour text type, and the writing text type
    outputprev = output;
    col1prev = col1;
    col2prev = col2;
    system("CLS");
    const char *light[4] = {"OFF", "RED", "BLUE", "GREEN"};     // defines the four light colours
    SetColor(col1);
    putchar('\n');
    int i;
    for (i=0;i<20;i++) {    // for loop to print a load of empty lines with the background colour 'col1'
        cout << "                                             \n";
    }   // end of for loop
    SetColor(7);
    cout << "\nColour - ";
    SetColor(col2);         // calls the function to change the console font colour (shows if run in cmd prompt, but not nppexec console)
    cout << light[output];
    SetColor(7);
    cout << " (Output " << output << ")\n(x=terminate, o=off, r=red, b=blue, g=green)";
}   //end PrintOut

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接