0

I have a python string:

my_string = 'thisisjustadummystringfortestingpurposes'

I want to print some regions of the string with color red, so I use ANSI escape codes:

  • red: '\x1b[31m'
  • black: '\x1b[0m'

Then what I do is, I create a new string with the ANSI codes inserted.

Example:

my_color_seq = seq[:5]       # 0:5 will be black
my_color_seq += '\x1b[31m'   # start red
my_color_seq += seq[5:10]    # 5:10 will be red
my_color_seq += '\x1b[0m'    # start black
my_color_seq += seq[10:]     # 10: will be black

print(my_color_seq)

This code works and correctly prints the region 5:10 red. I have even created a loop where I can use a list of indexes and lengths for the regions that I want colored with red.

Now the problem is that in my colored string contain non visible characters (the ANSI codes), so the index of the characters is no longer what it seems to be.

Imagine that I want to now process again the colored string by painting some other fragments in blue. I can no longer trust the indexes since my string is now larger than it seems.

Of course I can update the indexes to point the correct locations in the new string, but I was just wondering if there is an easier way of doing this.

Thomas Dickey
  • 51,086
  • 7
  • 70
  • 105
  • Python doesn't understand ANSI control characters so of course it will count them. I'd create a string wrapper and keep a character index value table at hand for the length - that way whenever you add an ANSI control structure, you add its length to the table at the character position and then it's trivial to retrieve 'modified' indexes via a simple `sum(index_table[:index])`. – zwer Dec 06 '17 at 15:09

1 Answers1

2

You could make a wrapper around the string coloring, and print the returned values while keeping the original string intact:

Not shown in the example, but you can slice the original string, to selectively pass the substrings to the wrapper, with appropriate kwarg values, and re-assemble the returned values in a new string that can be further processes, or printed.

def colorize(token, bold_and_al='', fg_color='', bg_color=''):
    prefix = '\x1b['
    suffix = 'm'
    reset = '\x1b[0m'

    return '\x1b[' + bold_and_al + ";" + fg_color + ";" + bg_color + "m" + token + reset


a = 'thisisjustadummystringfortestingpurposes'
print(colorize(a, bold_and_al='1', fg_color='34', bg_color='42'))

Edit:

Adding the example proposed by @MadPhysicist in the comments, to illustrate how the colorize wrapper could be used.:

print(''.join([colorize(a[:5], bold_and_al='1', fg_color='34', bg_color='42'), a[5:10], colorize(a[10:], bold_and_al='1', fg_color='32', bg_color='44')]))
Reblochon Masque
  • 35,405
  • 10
  • 55
  • 80
  • 1
    Or another way to say this would be, "if you want to preserve the original string, don't change it; change a copy instead." Good advice in general. – erik258 Dec 06 '17 at 15:08
  • 1
    Good thinking, however, in python, strings are immutable, so you never modify it, you always create copies. – Reblochon Masque Dec 06 '17 at 15:09
  • I think what OP is looking for is something like `''.join([colorize(a[:5], bold_and_al='1', fg_color='34', bg_color='42'), a[5:10], colorize(a[10:], bold_and_al='1', fg_color='32', bg_color='44')])` – Mad Physicist Dec 06 '17 at 15:17
  • Valid point, but in practice it doesn't really make a difference, since the variables we use to refer to the strings _are_ mutable. If your variable now points to a new string, and nothing points to the old string, it's functionally the same as the string changing. – erik258 Dec 06 '17 at 15:20
  • 1
    I see @DanFarrel, thank you for the precision. I was more thinking of keeping the data to be printed in a class; maybe a `__str__` or a `__repr__`, and pass this result alongside parameters to the wrapper to emphasize "stuff"... Like you would do, for instance, to underline which elements of an array have changed, or which ones have a critical value... – Reblochon Masque Dec 06 '17 at 15:26
  • @MadPhysicist, thank you for the example, I think it illustrates well how this could be used, & I added it to the answer. – Reblochon Masque Dec 06 '17 at 15:27