C++ - 覆盖以前输出到控制台的多行
C++ - Overwrite Multiple Lines that were Previously Output to Console
如果我在控制台中显示了一个字符网格,是否有任何实用的方法来重写这些多行以便在控制台的那些相同行上输出改变的网格。
例如,我想要这个代码:
#include <iostream>
using namespace std;
int main() {
for (int i=0; i<5; i++) {
for (int j=0; j<5; j++) {
cout << "-";
}
cout << endl;
}
getchar();
for (int i=0; i<5; i++) {
cout << '\r';
for (int j=0; j<5; j++) {
cout << "x";
}
cout.flush();
}
return 0;
}
输出:
-----
-----
-----
-----
-----
然后,根据用户输入,将其覆盖为;
xxxxx
xxxxx
xxxxx
xxxxx
xxxxx
我看到其他人通过输出'\r'重写单行来显示加载栏类型显示的其他示例,但我不确定是否有multiple lines?
的直接方法
我正在使用 MinGW。
一个解法:
#include <iostream>
#include <stdio.h>
#include <windows.h>
using namespace std;
void gotoxy( int column, int line )
{
COORD coord;
coord.X = column;
coord.Y = line;
SetConsoleCursorPosition(
GetStdHandle( STD_OUTPUT_HANDLE ),
coord
);
}
int main() {
for (int i=0; i<5; i++) {
for (int j=0; j<5; j++) {
cout << "-";
}
cout << endl;
}
getchar();
gotoxy(0,0);
for (int i=0; i<5; i++) {
for (int j=0; j<5; j++) {
cout << "x";
}
cout << endl;
}
return 0;
}
您可以使用SetConsoleCursorPosition
设置光标位置。
您可以在两个循环之间添加此代码块以清除屏幕。
printf("3[2J");
printf("3[%d;%dH", 0, 0);
它使用 ANSI 转义序列来执行此操作。您还需要添加 #include <stdio.h>
以支持 printf()
.
也不需要 \r
,您应该将 cout.flush();
替换为 cout << endl;
您的代码最终应该如下所示:
#include <iostream>
#include <stdio.h>
using namespace std;
int main()
{
for (int i=0; i<5; i++) {
for (int j=0; j<5; j++) {
cout << "-";
}
cout << endl;
}
getchar();
printf("3[2J");
printf("3[%d;%dH", 0, 0);
for (int i=0; i<5; i++) {
for (int j=0; j<5; j++) {
cout << "x";
}
cout<<endl;
}
return 0;
}
is any straightforward way to accomplish that for multiple lines?
是的。 Ansi 终端是一种简单易用的解决方案。我曾经使用过的每台 PC 都有 Ansi 终端仿真。在 ubuntu 上,gnome-terminal 工作正常。我长期以来一直使用 ansi 转义序列。
为此,您可能只需要转到和清除屏幕,但还有更多的 ansi 终端功能。
由于您已将此 post 标记为 C++,请考虑以下事项。
#include <chrono>
// 'compressed' chrono access --------------vvvvvvv
typedef std::chrono::high_resolution_clock HRClk_t; // std-chrono-hi-res-clk
typedef HRClk_t::time_point Time_t; // std-chrono-hi-res-clk-time-point
typedef std::chrono::milliseconds MS_t; // std-chrono-milliseconds
typedef std::chrono::microseconds US_t; // std-chrono-microseconds
typedef std::chrono::nanoseconds NS_t; // std-chrono-nanoseconds
using namespace std::chrono_literals; // support suffixes like 100ms, 2s, 30us
#include <iostream>
#include <iomanip>
#include <thread>
class Ansi_t // use ansi features of gnome-terminal,
{ // or any ansi terminal
// note: Ubuntu 15.10 gnome-terminal ansi term cursor locations
// are 1-based, with origin 1,1 at top left corner
enum ANSI : int { ESC = 27 }; // escape
public:
static std::string clrscr(void)
{
std::stringstream ss;
ss << static_cast<char>(ESC)<< "[H" // home
<< static_cast<char>(ESC)<< "[2J"; // clrbos
return(ss.str());
}
// r/c are 0 based------v------v------0 based from C++
static std::string gotoRC(int r, int c)
{
std::stringstream ss;
// Note: row/col of my apps are 0 based (as in C++)
// here I fix xy to 1 based, by adding 1 while forming output
ss << static_cast<char>(ESC)<< "["
<< (r+1) << ';' << (c+1) << 'H';
return(ss.str());
}
// tbr - add more ansi functions when needed
}; // Ansi_t
注意:我有时会添加断言来强制 r 和 c >= 0。
用法示例:
int main(int, char**)
{
int retVal = -1;
{
Time_t start_us = HRClk_t::now();
{
std::cout << Ansi_t::clrscr() << std::flush; // leaves cursor at top left of screen
for (int i=0; i<10; ++i)
{
for (int r=0; r<5; ++r) // 0 based
{
std::cout << Ansi_t::gotoRC(r+5, 5) // set cursor location
<< "-----" << std::flush;
}
std::this_thread::sleep_for(500ms);
// to overwrite
for (int r=0; r<5; ++r)
{
std::cout << Ansi_t::gotoRC(r+5, 5) // set cursor location
<< "xxxxx" << std::flush;
}
std::this_thread::sleep_for(500ms);
std::cout << Ansi_t::gotoRC(11,5) << 9-i << std::flush;
}// for i
std::cout << "\n\n" << std::endl;
return 0;
}
auto duration_us = std::chrono::duration_cast<US_t>(HRClk_t::now() - start_us);
std::cout << "\n\n duration " << duration_us.count() << " us" << std::endl;
}
return(retVal);
}
使用这些 Ansi_t 方法可以直接进行记忆。工作代码的示例片段:
- 将数据属性添加到 class
std::string m_toRowCol; // cursor position string
- 预填充ctor中的字符串
FOO_t::FOO_t( ... )
: m_seqId (a_gb.gBoardSz())
, m_toRowCol (a_gb.gotoRC(aRow, aCol)) // pre-compute
// other data attribute init
{
// ctor body
}
- 在状态输出之前使用预填充的字符串定位光标
m_gb.termUpdate(m_toRowCol + // position cursor
<output string>); // provide state info
如果我在控制台中显示了一个字符网格,是否有任何实用的方法来重写这些多行以便在控制台的那些相同行上输出改变的网格。
例如,我想要这个代码:
#include <iostream>
using namespace std;
int main() {
for (int i=0; i<5; i++) {
for (int j=0; j<5; j++) {
cout << "-";
}
cout << endl;
}
getchar();
for (int i=0; i<5; i++) {
cout << '\r';
for (int j=0; j<5; j++) {
cout << "x";
}
cout.flush();
}
return 0;
}
输出:
-----
-----
-----
-----
-----
然后,根据用户输入,将其覆盖为;
xxxxx
xxxxx
xxxxx
xxxxx
xxxxx
我看到其他人通过输出'\r'重写单行来显示加载栏类型显示的其他示例,但我不确定是否有multiple lines?
的直接方法我正在使用 MinGW。
一个解法:
#include <iostream>
#include <stdio.h>
#include <windows.h>
using namespace std;
void gotoxy( int column, int line )
{
COORD coord;
coord.X = column;
coord.Y = line;
SetConsoleCursorPosition(
GetStdHandle( STD_OUTPUT_HANDLE ),
coord
);
}
int main() {
for (int i=0; i<5; i++) {
for (int j=0; j<5; j++) {
cout << "-";
}
cout << endl;
}
getchar();
gotoxy(0,0);
for (int i=0; i<5; i++) {
for (int j=0; j<5; j++) {
cout << "x";
}
cout << endl;
}
return 0;
}
您可以使用SetConsoleCursorPosition
设置光标位置。
您可以在两个循环之间添加此代码块以清除屏幕。
printf("3[2J");
printf("3[%d;%dH", 0, 0);
它使用 ANSI 转义序列来执行此操作。您还需要添加 #include <stdio.h>
以支持 printf()
.
也不需要 \r
,您应该将 cout.flush();
替换为 cout << endl;
您的代码最终应该如下所示:
#include <iostream>
#include <stdio.h>
using namespace std;
int main()
{
for (int i=0; i<5; i++) {
for (int j=0; j<5; j++) {
cout << "-";
}
cout << endl;
}
getchar();
printf("3[2J");
printf("3[%d;%dH", 0, 0);
for (int i=0; i<5; i++) {
for (int j=0; j<5; j++) {
cout << "x";
}
cout<<endl;
}
return 0;
}
is any straightforward way to accomplish that for multiple lines?
是的。 Ansi 终端是一种简单易用的解决方案。我曾经使用过的每台 PC 都有 Ansi 终端仿真。在 ubuntu 上,gnome-terminal 工作正常。我长期以来一直使用 ansi 转义序列。
为此,您可能只需要转到和清除屏幕,但还有更多的 ansi 终端功能。
由于您已将此 post 标记为 C++,请考虑以下事项。
#include <chrono>
// 'compressed' chrono access --------------vvvvvvv
typedef std::chrono::high_resolution_clock HRClk_t; // std-chrono-hi-res-clk
typedef HRClk_t::time_point Time_t; // std-chrono-hi-res-clk-time-point
typedef std::chrono::milliseconds MS_t; // std-chrono-milliseconds
typedef std::chrono::microseconds US_t; // std-chrono-microseconds
typedef std::chrono::nanoseconds NS_t; // std-chrono-nanoseconds
using namespace std::chrono_literals; // support suffixes like 100ms, 2s, 30us
#include <iostream>
#include <iomanip>
#include <thread>
class Ansi_t // use ansi features of gnome-terminal,
{ // or any ansi terminal
// note: Ubuntu 15.10 gnome-terminal ansi term cursor locations
// are 1-based, with origin 1,1 at top left corner
enum ANSI : int { ESC = 27 }; // escape
public:
static std::string clrscr(void)
{
std::stringstream ss;
ss << static_cast<char>(ESC)<< "[H" // home
<< static_cast<char>(ESC)<< "[2J"; // clrbos
return(ss.str());
}
// r/c are 0 based------v------v------0 based from C++
static std::string gotoRC(int r, int c)
{
std::stringstream ss;
// Note: row/col of my apps are 0 based (as in C++)
// here I fix xy to 1 based, by adding 1 while forming output
ss << static_cast<char>(ESC)<< "["
<< (r+1) << ';' << (c+1) << 'H';
return(ss.str());
}
// tbr - add more ansi functions when needed
}; // Ansi_t
注意:我有时会添加断言来强制 r 和 c >= 0。
用法示例:
int main(int, char**)
{
int retVal = -1;
{
Time_t start_us = HRClk_t::now();
{
std::cout << Ansi_t::clrscr() << std::flush; // leaves cursor at top left of screen
for (int i=0; i<10; ++i)
{
for (int r=0; r<5; ++r) // 0 based
{
std::cout << Ansi_t::gotoRC(r+5, 5) // set cursor location
<< "-----" << std::flush;
}
std::this_thread::sleep_for(500ms);
// to overwrite
for (int r=0; r<5; ++r)
{
std::cout << Ansi_t::gotoRC(r+5, 5) // set cursor location
<< "xxxxx" << std::flush;
}
std::this_thread::sleep_for(500ms);
std::cout << Ansi_t::gotoRC(11,5) << 9-i << std::flush;
}// for i
std::cout << "\n\n" << std::endl;
return 0;
}
auto duration_us = std::chrono::duration_cast<US_t>(HRClk_t::now() - start_us);
std::cout << "\n\n duration " << duration_us.count() << " us" << std::endl;
}
return(retVal);
}
使用这些 Ansi_t 方法可以直接进行记忆。工作代码的示例片段:
- 将数据属性添加到 class
std::string m_toRowCol; // cursor position string
- 预填充ctor中的字符串
FOO_t::FOO_t( ... ) : m_seqId (a_gb.gBoardSz()) , m_toRowCol (a_gb.gotoRC(aRow, aCol)) // pre-compute // other data attribute init { // ctor body }
- 在状态输出之前使用预填充的字符串定位光标
m_gb.termUpdate(m_toRowCol + // position cursor <output string>); // provide state info