js091514
js091514

Reputation: 165

c++ ansi escape codes not displaying color to console

I am trying to change the color of text in the console.

We are supposed to use a config file to read the ansi escape codes from:

this is what's in my file

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

Here is my code:

    #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;
}

Not sure why but only the last print statement actually displays in color the other outputs the ansi escape codes as text.

Here is my output :

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

Upvotes: 5

Views: 5209

Answers (3)

user760453
user760453

Reputation: 201

Instead of doing this:

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

C++ offers possibility to write your own stream manipilator like std::endl to handle escape code.

The goal is to write code like this that seems more readable for me:

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

For writing your own stream manipulators you can proceed like this for instance (simple raw implementation, but you will require to take value from your map...):

#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

Alternatively, C offers possiblity to use #define to create constants.

#define foreground_magenta "\033[35m"

Upvotes: 6

R Sahu
R Sahu

Reputation: 206577

The problem with reading the config.txt files is that the string is read as though it is assigned to be:

std::string str = "\\033[0;31m";

i.e. the \ is treated as a character. What you need in code is "\033", i.e. the character represented by the octal number 033.

You can change the following lines in your code to ignore the "\\033" part of the string and print the octal number.

    cout << x.second << x.first <<endl;

needs to be:

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

With that change, I tried your program on my desktop and it works as expected.

Upvotes: 2

MikeCAT
MikeCAT

Reputation: 75062

The escape sequence in the file won't converted to ESC character.

Moreover, your file has common parts.

For that reason, I moved the common parts that contain ESC character to program and had the configuration file have only the color codes.

File:

red     31      #red
blue    34      #blue
green   32      #green
grey    37      #grey

Program:

#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;
}

Upvotes: 1

Related Questions