atancasis has asked for the wisdom of the Perl Monks concerning the following question:

I need some advice on how to properly handle strings with ANSI color escape sequences (eg: \033[33m) for printing to an ncurses window. Adding such strings to the ncurses window causes the escape sequences to be left intact and be printed out along with the message.

I'm thinking I might have to resort to creating a parser such that upon encountering an escape sequence, corresponding attron()/attroff() calls are made prior to calling printw()/addstr(). However, such an implementation will be inefficient and I'm sure there's a better way to do this.

Any help and guidance would be greatly appreciated. Thanks.

  • Comment on Handling ANSI Escape Sequences for Printing to Curses

Replies are listed 'Best First'.
Re: Handling ANSI Escape Sequences for Printing to Curses
by GrandFather (Saint) on Jul 31, 2010 at 22:36 UTC

    Show us the code you have tried.

    True laziness is hard work

      Code below produces the following output (escape sequence appearing as string): 'a ^[[33YELLOW^[[0m word'

      use Curses; use Term::ANSIColor; initscr(); addstr(stdscr, "a ".color('yellow')."YELLOW".color('reset')." word"); refresh(); getch(); endwin();

      Implementing a parser, with the following code, will produce the desired effects but will be expensive. Not to mention I feel like I'm reinventing the wheel.

      use Curses; use Term::ANSIColor; initscr(); start_color(); init_pair(1, COLOR_YELLOW, COLOR_BLACK); formatted_text(stdscr, "a ".color('yellow')."YELLOW".color('reset')." +word"); refresh(); getch(); endwin(); sub formatted_text { my ( $win, $text ) = @_; # tokenize text on escape sequences foreach ( split(/(\e\[[^m]+m)/, $text) ) { # token starts with <ESC>, set attr and dont add text if ( /^\e/ ) { if ( $_ =~ quotemeta(color('yellow')) ) { attron(COLOR_PAIR(1)); } elsif ( $_ =~ quotemeta(color('reset')) ) { attroff(COLOR_PAIR(1)); } next; } addstr($win, $_); } }
          Implementing a parser, with the following code, will produce the desired effects but will be expensive.

        Expensive? How much text are you putting out to the screen before the "inefficiency" is noticeable? It's a little premature to be optimizing at this time.

          Not to mention I feel like I'm reinventing the wheel.

        But consider what you were doing with the code you posted. You want to attron a color pair each time you encounter a yellow escape sequence. That seems like quite a customized requirement.

        You're using Curses and Term::ANSIColor, so you're leveraging a lot already = good for you! I did a cursory search and couldn't find any more helpful modules beyond those two.

        I'd write formatted_text() using a dispatch hash. Also made the regex quantifier non-greedy:
        my %esc2action = ( color('yellow') => sub { attron(COLOR_PAIR(1)) }, color('reset') => sub { attroff(COLOR_PAIR(1)) }, ); sub formatted_text { my ($win, $text) = @_; # tokenize text on escape sequences for my $token (split /(\e\[[^m]+?m)/, $text) { my $act = $esc2action{$token}; if ($act) { $act->(); } else { addstr($win, $token); } } }
        For a yellow "word"...
        #!/usr/bin/perl use strict; use warnings; use Term::ANSIColor::Markup; my $text = qq{ <yellow>word</yellow> }; print Term::ANSIColor::Markup->colorize($text);