【问题标题】:Ncurses displays garbage when using printf in while loop在 while 循环中使用 printf 时 Ncurses 显示垃圾
【发布时间】:2020-01-07 20:33:48
【问题描述】:

我正在 Linux 上创建一个 Snake 游戏。当我意识到我需要一个包含在ncruses.h 中的getch() 函数时,我一直在使用iostream。我有一个绘图方法,它在使用std::cout 时将所有板元素打印到控制台,并且每个字符都正确显示。我有整个董事会。但是当我切换到 ncurses 并将所有 std::cout 重写为 printf() 时,程序开始只编写电路板的一部分。这一切都是在while循环中完成的,每个循环板都被重新绘制。

我是新手,请善待。

这是我的 main.cpp

#include <iostream>
#include <chrono>
#include <thread>
#include <ncurses.h>
#include <stdio.h>

#include "board.h"
#include "consoledraw.h"
#include "snake.h"
#include "fruit.h"

void sleepThread()
{
static constexpr int SLEEP_MS = 1000;

std::this_thread::sleep_for(std::chrono::milliseconds(SLEEP_MS));
}

int main()
{
std::cout << "\e[8;30;140t"; // set console window size

Board _board;
Snake _snake;
consoledraw draw;
Fruit _fruit;

_board.setBoardSize(64, 36); // cca 16:9
_board.setDefaultItemType();
_board.makeSnake(_snake);
_board.setSnakeOnBoard(_snake);
_board.makeFruit(_fruit);
_board.setFruitOnBoard(_fruit);

int score = 0;

initscr();
setlocale(LC_ALL, "");
// raw();
cbreak();
curs_set(0);
noecho();
scrollok(stdscr, TRUE);
keypad(stdscr, TRUE);

while (true)
{
    draw.redraw(_board);
    // printf("Your score is: \r\v"); // TODO << score << std::endl;
    sleepThread();
    _board.clearBoard();
    _snake.moveSnake(_snake);

    if (_snake.checkSelfCollision(_snake) == true)
    {
        draw.clearConsole();
        printf("Game Over!\n");
        return 0;
    };

    if (_snake.eatFood(_fruit) == true)
    {
        _board.makeFruit(_fruit);
        _board.setFruitOnBoard(_fruit);
        score++;
    }
    _board.setSnakeOnBoard(_snake);
    _board.setFruitOnBoard(_fruit);
}
endwin();
return 0;
}

这是我的 consoledraw.cpp

#include "consoledraw.h"

consoledraw::consoledraw()
{

}

void consoledraw::redraw(Board &board)
{
clearConsole();
drawBoard(board);
}

void consoledraw::clearConsole()
{
// Kompletne vymaze celou konzoli a nasrtavi kurzor do leveho horniho rohu.
refresh();
}

void consoledraw::drawBoard(Board &board)
{
auto b = board.getBoard();

// Top border line
drawHorizontalBorderLine(b[0].size(), true);

// Board
for (unsigned int y = 0; y < b.size(); y++)
{
    printf(LINE_VERTICAL); // border line

    for (unsigned int x = 0; x < b[y].size(); x++)
    {
        drawSymbol(b[y][x]);
    }
    printf(LINE_VERTICAL"\r\v"); // border line + newline
}

// Down border line
drawHorizontalBorderLine(b[0].size(), false);
}

void consoledraw::drawSymbol(ItemType itemType)
{
if (itemType == ItemType::Empty)
{
    printf(BLOCK_TRANSPARENT);
}
else if (itemType == ItemType::Tsss)
{
    printf(BLOCK_MEDIUM_SHADE);
}
else if (itemType == ItemType::Food)
{
    printf(BLOCK_LIGHT_SHADE);
}
else if (itemType == ItemType::TsssHead)
{
    printf(BLOCK_DARK_SHADE);
}
}

void consoledraw::drawHorizontalBorderLine(unsigned long boardWidth, bool isTop)
{
// Left corner
if (isTop)
{
    printf(LINE_UPLEFT_CORNER);
}
else
{
    printf(LINE_DOWNLEFT_CORNER);
}

// Line
for (unsigned int i = 0; i < boardWidth; i++)
{
    printf(LINE_HORIZONTAL);
}

// Right corner
if (isTop)
{
    printf(LINE_UPRIGHT_CORNER);
}
else
{
    printf(LINE_DOWNRIGHT_CORNER);
}

printf("\r\v");
}

这是我在使用 ncurses 时得到的结果:

这是我使用 iostream 得到的结果:

【问题讨论】:

  • 不要使用iostream! ncurses 有自己的输入/输出方法。查看this awesome tutorial 了解更多信息。
  • 一般不要混用不同的IO方法。选择一个并坚持下去。如果两个不同的 IO 方法发生冲突,则不值得整理错误,如果他们花时间确保它们不发生冲突,则不值得对性能造成影响。

标签: c++ linux console ncurses


【解决方案1】:

C 库 (printf()) 和 C++ 库 (std::cout) 缓冲它们的输出 - 尽管默认情况下 C 和 C++ 库会同步它们的缓冲区。

ncurses 库执行原始(无缓冲)输出,有时用带内控制序列包装。如果您通过 C 或 C++ 库和 ncurses 库将输出混合到同一设备,您的屏幕上就会出现垃圾。就像你一样。

您应该使用 ncurses 库或标准库来处理来自终端的输入/输出。不要混合它们。

【讨论】:

  • 我认为只有在打印到默认的“stdscr”窗口时才能混合 (printf()) 和 (std::cout)。我只是在调整控制台窗口的大小时使用 std::cout 并且也远离这个提到的窗口。肯定会尝试你的建议。谢谢!我将尝试完全摆脱 iostream,然后发布我的尝试结果,让您知道这是否有效。
  • 所以我完全摆脱了 iostream 并且仍然遇到同样的问题。请看链接:ibb.co/n3VpZhr
  • 编辑:它将绘制整个板,但我只能在以前的周期中看到。不在当前绘制的棋盘中。
  • 要么 1) 您在循环的开始而不是结束时调用 redraw(board),或者 2) 您需要在某处刷新缓冲区。
  • 我尝试了第一个选项以调用 redraw(board) 作为结尾,但没有成功。仍然在屏幕上显示棋盘的那些随机部分。至于第二个选项,我想我明白你的意思,但不知道该怎么做。我找到了这个页面,它解释得很好,但是我不能使用 iostream,由于 ncureses 的使用,现在有点被禁止。 geeksforgeeks.org/buffer-flush-means-c
猜你喜欢
  • 2016-07-08
  • 1970-01-01
  • 1970-01-01
  • 2021-02-06
  • 2017-07-26
  • 1970-01-01
  • 2014-04-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多