简体   繁体   中英

How can I parse Linux terminal colour codes?

I'm trying to display coloured text form a file in a curses python app. The file contains 24 bit colour escape codes such as: ^[[48;2;255;255;255m^[[38;2;255;255;255mA . I need to loop through the file and generate a list of tuples, containing tuples like so: ((background: r, g, b), (foreground: r, g, b), char) . Here is the code I have tried so far. It tries to find all the 0x27 bytes and parse them, however it does not seem to work, and not only that it is not particularly safe/scalable/maintainable. How can I parse the colour codes in a file like this? Is there a library? How can I improve this code, if there is no library?

def read_until(char, data, pos):
    """read from a bytes-like object starting at `pos` until the target character is found"""
    start = pos
    while data[pos] != char: pos += 1
    return data[start:pos], pos

def makeData(data):
    assert type(data) == bytes

    out = []
    pos = 0
    bg = (0, 0, 0)
    fg = (255, 255, 255)

    while pos < len(data):
        if data[pos] == 27: #start of the escape sequence
            pos += 2 #+2 to ignore the `[` char
            if data[pos:pos + 2] == b"0m": #reset character, just ignore it
                continue
            first_num, pos = read_until(ord(";"), data, pos)
            second_num, pos = read_until(ord(";"), data, pos + 1) #+ 1 for the `;` char

            r, pos = read_until(ord(";"), data, pos + 1)
            g, pos = read_until(ord(";"), data, pos + 1)
            b, pos = read_until(ord("m"), data, pos + 1)
            r = int(r)
            g = int(g)
            b = int(b)
            pos += 1 #skip last `m` char
            if first_num == b"48": #48 means foreground
                fg = (r, g, b)
            elif first_num == b"38": #38 means background
                bg = (r, g, b)
        else:
            out.append((fg, bg, chr(data[pos]))) #use current fg and bg colours with char
            pos += 1
    return out

with open("file.txt", "rb") as f:
    print("".join([chr(i[2]) for i in makeData(f.read())])) #print without colour codes, to test

You can use regular expression to parse the data:

import re
with open("file.txt") as f:
    result = re.findall(r'\x1b\[48;2;(\d+);(\d+);(\d+)m\x1b\[38;2;(\d+);(\d+);(\d+)m(.)', f.read())
    print(result)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM