C++ ANSI转义码无法在控制台显示颜色

5

我正在尝试更改控制台中文本的颜色。

我们应该使用配置文件从中读取ansi转义代码:

这是我的文件中的内容

red     \033[0;31m      #red
blue    \033[0;34m      #blue
green   \033[0;32m      #green
grey    \033[0;37m      #grey

这是我的代码:
    #include <iostream> 
    #include <sstream>
    #include <iomanip>
    #include <string>
    #include <fstream>
    #include <map>
    using namespace std;

int main(int argc, char * argv[]){
    string file = "config.txt";
    string line = "";
    string tag = "";
    string ansi = "";
    map <string, string> m;

    if(argc == 2){  
        file = argv[1];
    }
    ifstream in(file, ios_base::in | ios_base::binary);
    if(!in){
        cerr<<"could not open file";
    }

    while (getline(in, line)){
        istringstream iss(line);
        if(iss>>tag>>ansi){
            auto it = m.find(tag);
            if(it == m.end()){
                m.insert(make_pair(tag,ansi));
            }

        }
    }

    for(auto x: m){
        cout<<x.second<<x.first<<endl;
    }
    cout<<"\033[0;35mhello";
    return 0;
}

不确定为什么,但只有最后一个print语句实际上以颜色显示,其他输出将ANSI转义码作为文本输出。

这是我的输出:

\033[0;34mblue
\033[0;32mgreen
\033[0;37mgrey
\033[0;31mred
hello (in purple)

1
我必须在这里进行相当多的猜测,但我认为您假设将文本文件中的\033读入C++程序时会被解释为ESC。然而,事实并非如此。反斜杠只有对编译器运行的解析器有意义。对于文本输入,您作为程序员需要将\033视为ESC。对于输入函数而言,它只是字符串{'\\', '0', '3', '3'} - 5gon12eder
你需要自己解析和转换文件中的转义序列,或者修改配置文件的格式。 - MikeCAT
我猜就是这样...那么我该怎么做呢? - js091514
2
你的配置文件格式是固定的吗?最简单的解决方案是让 CSI(控制序列引入,即\033[)隐式地添加到程序中。这也会使配置文件看起来更简单。 - 5gon12eder
是的,我认为这门课程的讲师对格式要求相当严格。我将忽略序列的前四个字符,并在其余序列之前手动打印转义序列。它有效! :) - js091514
从文件中读取 \033[0;31m 不同于在 C 字符串中定义它为 _"\033[0;31m"_。要看到区别,请读取该行并定义字符串,然后以十六进制转储它们。你会看到它们之间的差异。 - Craig Estey
3个回答

5

不要这样做:

cout<<"\033[0;35mhello";

C++ 提供了自定义流操作符的可能性,比如像 std::endl 一样处理转义码。

我的目标是编写更易读的代码,比如下面这样:

cout << ansi::foreground_magenta << "hello" ;

如果你想编写自己的流操作符,可以按照以下步骤进行(这里提供一个简单的原始实现,但你需要从自己的映射表中获取值...):

#include <ostream>

namespace ansi {
  template < class CharT, class Traits >
  constexpr
  std::basic_ostream< CharT, Traits > & reset( std::basic_ostream< CharT, Traits > &os )
  {
     return os << "\033[0m";
  }

  template < class CharT, class Traits >
  constexpr
  std::basic_ostream< CharT, Traits > & foreground_black( std::basic_ostream< CharT, Traits > &os )
  {
     return os << "\033[30m";
  }

  template < class CharT, class Traits >
  constexpr
  std::basic_ostream< CharT, Traits > & foreground_red( std::basic_ostream< CharT, Traits > &os )
  {
     return os << "\033[31m";
  }
  ...
 } // ansi

另外,C语言可以使用 #define 来创建常量。

#define foreground_magenta "\033[35m"

2
读取config.txt文件的问题在于,字符串被读取时似乎被赋值为:
std::string str = "\\033[0;31m";

\被视为一个字符。在代码中需要的是"\033",即由八进制数字033表示的字符。
您可以更改代码中的以下行以忽略字符串中的"\\033"部分并打印八进制数字。
    cout << x.second << x.first <<endl;

需要:

    cout << '\033' << x.second.substr(4) << x.first <<endl;

进行了这个更改后,我在我的桌面上尝试了你的程序,结果符合预期。


1
文件中的转义序列不会被转换为ESC字符。
此外,您的文件有共同的部分。
因此,我将包含ESC字符的共同部分移动到程序中,并使配置文件仅具有颜色代码。
文件:
red     31      #red
blue    34      #blue
green   32      #green
grey    37      #grey

程序:

#include <iostream> 
#include <sstream>
#include <iomanip>
#include <string>
#include <fstream>
#include <map>
using namespace std;

int main(int argc, char * argv[]){
    string file = "config.txt";
    string line = "";
    string tag = "";
    string ansi = "";
    map <string, string> m;

    if(argc == 2){  
        file = argv[1];
    }
    ifstream in(file, ios_base::in | ios_base::binary);
    if(!in){
        cerr<<"could not open file";
    }

    while (getline(in, line)){
        istringstream iss(line);
        if(iss>>tag>>ansi){
            auto it = m.find(tag);
            if(it == m.end()){
                m.insert(make_pair(tag,ansi));
            }

        }
    }

    for(auto x: m){
        cout<<"\033[0;"<<x.second<<"m"<<x.first<<endl;
    }
    cout<<"\033[0;35mhello";
    return 0;
}

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