操纵标准输出流以水平打印多行字符串

发布于 2025-01-26 13:02:50 字数 457 浏览 3 评论 0原文

因此,我有3个字符串,这些弦应该占据3行。我认为这是代表我的弦的好方法:

std::string str1 = "███████\n███1███\n███████";
std::string str2 = "███████\n███2███\n███████";
std::string str3 = "███████\n███3███\n███████";

但是我意识到,当我这样做并只想说琴弦时,它们会彼此印在我不想要的彼此上。我希望输出看起来像这样:

█████████████████████
███1██████2██████3███
█████████████████████

如何实现这种效果?我只知道设置要操纵输出,但是我不知道在这里有什么帮助。

注意:我将将这些存储在数组中,而不是在数组上循环并打印它们,我觉得这可能也可以改变解决方案。

So I have three strings and these strings are supposed to occupy 3 lines. I thought this was a good way to represent my string:

std::string str1 = "███████\n███1███\n███████";
std::string str2 = "███████\n███2███\n███████";
std::string str3 = "███████\n███3███\n███████";

But I realise that when I do this and just cout the strings, they get printed on top of each other which is not I want. I want the output to look like this:

█████████████████████
███1██████2██████3███
█████████████████████

How can I achieve this effect? I only know setw to manipulate the output however I don't know how that could help here.

note: I will have these stored in an array and than loop over the array and print them, I feel like that might change the solution a bit as well.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

风向决定发型 2025-02-02 13:02:50

如果您确定您的输出将始终显示在现代终端上,以支持“ “ nofollow noreferrer”>然后,您可以利用它来发挥自己的优势。

#include <iostream>
#include <sstream>
#include <string>
#include <vector>


// helper: split a string into a list of views
auto splitv( const std::string & s, const std::string & separators )
{
  std::vector <std::string_view> views;
  std::string::size_type a = 0, b = 0;
  while (true)
  {
    a = s.find_first_not_of( separators, b );
    b = s.find_first_of    ( separators, a );
    if (a >= s.size()) break;
    if (b == s.npos) b = s.size();
    views.emplace_back( &(s[a]), b-a );
  }
  return views;
}


std::string print( const std::string & s )
{
  std::ostringstream os;
  for (auto sv : splitv( s, "\n" ))
    os
      << "\033" "7" // DEC save cursor position
      << sv
      << "\033" "8" // DEC restore cursor position
      << "\033[B";  // move cursor one line down
  return os.str().substr( 0, os.str().size()-5 );
}


std::string movexy( int dx, int dy )
{
  std::ostringstream os;
  if      (dy < 0) os << "\033[" << -dy << "A";
  else if (dy > 0) os << "\033[" <<  dy << "B";
  if      (dx > 0) os << "\033[" <<  dx << "C";
  else if (dx < 0) os << "\033[" << -dx << "D";
  return os.str();
}


int main()
{
  std::string str1 = "███████\n███1███\n███████";
  std::string str2 = "███████\n███2███\n███████";
  std::string str3 = "███████\n███3███\n███████";
  
  std::cout 
    << "\n" "\n\n"  // blank line at top +  blocks are three lines high
    << movexy( 2, -2 ) << print( str1 )  // first block is placed two spaces from left edge
    << movexy( 1, -2 ) << print( str2 )  // remaining blocks are placed one space apart
    << movexy( 1, -2 ) << print( str3 )
    << "\n\n";      // newline after last block, plus extra blank line at bottom
}

这会产生输出:

  ███████ ███████ ███████
  ███1███ ███2███ ███3███
  ███████ ███████ ███████

当然,间距的添加是完全可选的,仅是出于示范目的而添加的。

优点:UTF-8和漂亮的颜色!

这种方法的优点是,您不必为包含多字节字符的字符串(UTF-8,如您所做的)或其他任何其他信息(例如终端颜色序列< /strong> 。

也就是说,您可以通过将颜色序列添加到每个strn变量来对每个块的颜色不同! (警告是您必须在每个新线上重复一个颜色序列。 >这是各种终端的已知问题... )

  // red, white, and blue
  std::string str1 = "\033[31m███████\n\033[31m███1███\n\033[31m███████";
  std::string str2 = "\033[37m███████\n\033[37m███2███\n\033[37m███████";
  std::string str3 = "\033[34m███████\n\033[34m███3███\n\033[34m███████";

相对与绝对的看法,将

另一个警告定位到此特殊示例是您必须意识到文本caret的位置(“光标”)每次输出后结束。您也可以使用终端逃生序列在每个输出之前绝对将其定位。

std::string gotoxy( int x, int y )
{
  std::ostringstream os;
  os << "\033[" << y << ";" << x << "H";
  return os.str();
}

那么,您就不必关心脑袋最终到达的位置。只需在打印之前指定绝对位置即可。只是不要让文字滚动!

Windows OS注意事项

最终,如果您在Windows上并使用旧的Windows控制台,则您必须 必须 初始化ANSI终端序列的终端 utf的终端-8输出:

#ifdef _WIN32
  #include <windows.h>

  void init_terminal()
  {
    DWORD mode;
    HANDLE hStdOut = GetStdHandle( STD_OUTPUT_HANDLE );
    GetConsoleMode( hStdOut, &mode );
    SetConsoleMode( hStdOut, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING );
    SetConsoleOutputCP( 65001 );
  }
#else
  void init_terminal() { }
#endif

int main()
{
  init_terminal();
  ...

这对新的Windows终端没有损害。我建议您这样做,只是因为您不知道用户将用来运行您的程序中的哪一个中的哪一个。

If you are certain that your output will always be displayed on a modern terminal supporting “ANSI Escape Codes” then you can use that to your advantage.

#include <iostream>
#include <sstream>
#include <string>
#include <vector>


// helper: split a string into a list of views
auto splitv( const std::string & s, const std::string & separators )
{
  std::vector <std::string_view> views;
  std::string::size_type a = 0, b = 0;
  while (true)
  {
    a = s.find_first_not_of( separators, b );
    b = s.find_first_of    ( separators, a );
    if (a >= s.size()) break;
    if (b == s.npos) b = s.size();
    views.emplace_back( &(s[a]), b-a );
  }
  return views;
}


std::string print( const std::string & s )
{
  std::ostringstream os;
  for (auto sv : splitv( s, "\n" ))
    os
      << "\033" "7" // DEC save cursor position
      << sv
      << "\033" "8" // DEC restore cursor position
      << "\033[B";  // move cursor one line down
  return os.str().substr( 0, os.str().size()-5 );
}


std::string movexy( int dx, int dy )
{
  std::ostringstream os;
  if      (dy < 0) os << "\033[" << -dy << "A";
  else if (dy > 0) os << "\033[" <<  dy << "B";
  if      (dx > 0) os << "\033[" <<  dx << "C";
  else if (dx < 0) os << "\033[" << -dx << "D";
  return os.str();
}


int main()
{
  std::string str1 = "███████\n███1███\n███████";
  std::string str2 = "███████\n███2███\n███████";
  std::string str3 = "███████\n███3███\n███████";
  
  std::cout 
    << "\n" "\n\n"  // blank line at top +  blocks are three lines high
    << movexy( 2, -2 ) << print( str1 )  // first block is placed two spaces from left edge
    << movexy( 1, -2 ) << print( str2 )  // remaining blocks are placed one space apart
    << movexy( 1, -2 ) << print( str3 )
    << "\n\n";      // newline after last block, plus extra blank line at bottom
}

This produces the output:

  ███████ ███████ ███████
  ███1███ ███2███ ███3███
  ███████ ███████ ███████

The addition of spacing is, of course, entirely optional and only added for demonstrative purposes.

Advantages: UTF-8 and Pretty colors!

The advantage to this method is that you do not have to store or otherwise take any special care for strings containing multi-byte characters (UTF-8, as yours does) or any additional information like terminal color sequences.

That is, you could color each of your blocks differently by adding a color sequence to each strN variable! (The caveat is that you must repeat a color sequence after every newline. This is a known problem with various terminals...)

  // red, white, and blue
  std::string str1 = "\033[31m███████\n\033[31m███1███\n\033[31m███████";
  std::string str2 = "\033[37m███████\n\033[37m███2███\n\033[37m███████";
  std::string str3 = "\033[34m███████\n\033[34m███3███\n\033[34m███████";

Relative vs Absolute Caret Positioning

The other caveat to this particular example is that you must be aware of where the text caret (“cursor”) ends-up after each output. You could also use terminal escape sequences to absolutely position the caret before every output.

std::string gotoxy( int x, int y )
{
  std::ostringstream os;
  os << "\033[" << y << ";" << x << "H";
  return os.str();
}

Then you wouldn’t have to care where the caret ends up. Just specify an absolute position before printing. Just don’t let the text scroll!

Windows OS Considerations

Finally, if you are on Windows and using the old Windows Console, you must initialize the terminal for ANSI terminal sequences and for UTF-8 output:

#ifdef _WIN32
  #include <windows.h>

  void init_terminal()
  {
    DWORD mode;
    HANDLE hStdOut = GetStdHandle( STD_OUTPUT_HANDLE );
    GetConsoleMode( hStdOut, &mode );
    SetConsoleMode( hStdOut, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING );
    SetConsoleOutputCP( 65001 );
  }
#else
  void init_terminal() { }
#endif

int main()
{
  init_terminal();
  ...

This does no harm to the new Windows Terminal. I recommend you do it either way just because you do not know which of the two your user will use to run your program, alas.

客…行舟 2025-02-02 13:02:50

将每个卡的行作为元素存储在数组中。这很容易。

#include <iostream>

int main()
{
    const char * str1[3] = {"███████","███1███","███████"};
    const char * str2[3] = {"███████","███2███","███████"};
    const char * str3[3] = {"███████","███3███","███████"};

    for( int row = 0; row < 3; row ++ )
    {
        std::cout << str1[row] << str2[row] << str3[row] << "\n";
    }
}

输出:

█████████████████████
███1██████2██████3███
█████████████████████

同样,如果需要的话,在这些空间之间添加一个空间非常容易。

Store the rows of each card as elements in an array. That makes it pretty easy.

#include <iostream>

int main()
{
    const char * str1[3] = {"███████","███1███","███████"};
    const char * str2[3] = {"███████","███2███","███████"};
    const char * str3[3] = {"███████","███3███","███████"};

    for( int row = 0; row < 3; row ++ )
    {
        std::cout << str1[row] << str2[row] << str3[row] << "\n";
    }
}

Output:

█████████████████████
███1██████2██████3███
█████████████████████

Again, pretty easy to add a space between those, if you want.

过潦 2025-02-02 13:02:50

您可以在\ n上将每个分割并打印它们。我们可以使用std :: stringstream用于通过\ n拆分。

void print(std::array<std::string, 3>& arr){
    std::vector<std::stringstream> arr_buf{};
    arr_buf.reserve(arr.size());

    for(auto& str: arr){
        arr_buf.emplace_back(str);
    }

    for(auto i=0u; i < arr.size(); ++i){
        for(auto& stream: arr_buf){
            std::string t;
            stream >> t;
            std::cout << t ;
        }
        std::cout << "\n";
    }
}

输出:

print(arr)

█████████████████████
███1██████2██████3███
█████████████████████

链接到demo

You could split each on \n and print them. We can use std::stringstream for splitting by \n.

void print(std::array<std::string, 3>& arr){
    std::vector<std::stringstream> arr_buf{};
    arr_buf.reserve(arr.size());

    for(auto& str: arr){
        arr_buf.emplace_back(str);
    }

    for(auto i=0u; i < arr.size(); ++i){
        for(auto& stream: arr_buf){
            std::string t;
            stream >> t;
            std::cout << t ;
        }
        std::cout << "\n";
    }
}

Output:

print(arr)

█████████████████████
███1██████2██████3███
█████████████████████

Link to Demo

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文