Frage

Ich habe einig Python-Code, der automatisch eine Reihe von Daten in einem schönen Spaltenformat gedruckt wird, auch in den entsprechenden ASCII-Escape-Sequenzen zu Farbe verschiedene Stücke der Daten für die Lesbarkeit setzen.

ich am Ende schließlich oben mit jeder Zeile als Liste dargestellt werden, wobei jedes Element eine Säule ist, die so platz gepolstert ist, dass die gleichen Spalten in jeder Zeile sind immer gleich lang. Leider wenn ich dies tatsächlich zu drucken, nicht alle Spalten in einer Reihe aufstellen. Ich vermute, dies ist mit den ASCII-Escape-Sequenzen zu tun - weil die len Funktion nicht diese zu erkennen scheint:

>>> a = '\x1b[1m0.0\x1b[0m'
>>> len(a)
11
>>> print a
0.0

Während also jede Spalte die gleiche Länge nach len, sie sind nicht wirklich die gleiche Länge haben, wenn sie auf dem Bildschirm ausgegeben.

Gibt es eine Möglichkeit (mit Ausnahme einige Hacks mit regulären Ausdrücken zu tun, die ich lieber nicht tun), um die Escape-Sequenzen zu nehmen und herauszufinden, was die gedruckte Länge ist so kann ich Raum Pad angemessen? Vielleicht irgendwie nur „drucken“, um es String zurück und die Länge, dass untersuchen?

War es hilfreich?

Lösung

Das pyparsing Wiki enthält diesen hilfreich Ausdruck für den Abgleich auf ANSI-Escape-Sequenzen:

ESC = Literal('\x1b')
integer = Word(nums)
escapeSeq = Combine(ESC + '[' + Optional(delimitedList(integer,';')) + 
                oneOf(list(alphas)))

Hier ist, wie dies machen in einen Fluchtsequenz-Stripper:

from pyparsing import *

ESC = Literal('\x1b')
integer = Word(nums)
escapeSeq = Combine(ESC + '[' + Optional(delimitedList(integer,';')) + 
                oneOf(list(alphas)))

nonAnsiString = lambda s : Suppress(escapeSeq).transformString(s)

unColorString = nonAnsiString('\x1b[1m0.0\x1b[0m')
print unColorString, len(unColorString)

druckt:

0.0 3

Andere Tipps

Das verstehe ich nicht zwei Dinge.

(1) Es ist der Code, unter Ihrer Kontrolle. Sie wollen Escape-Sequenzen, um Ihre Daten hinzufügen und sie dann wieder abzustreifen, so dass Sie die Länge Ihrer Daten berechnen kann ?? Es scheint viel einfacher, die Polsterung vor berechnen Addieren der Escape-Sequenzen. Was bin ich?

Lassen Sie uns annehmen, dass keine der Escape-Sequenzen die Cursorposition zu ändern. Wenn sie das tun, wird die derzeit akzeptierte Antwort sowieso nicht funktionieren.

Nehmen wir an, dass Sie die Zeichenfolge-Daten für jede Spalte haben (vor Escape-Sequenzen hinzugefügt) in einer Liste namens string_data und den vorgegebenen Spaltenbreiten sind in einer Liste namens width. Probieren Sie etwas wie folgt aus:

temp = []
for colx, text in enumerate(string_data):
    npad = width[colx] - len(text) # calculate padding size
    assert npad >= 0
    enhanced = fancy_text(text, colx, etc, whatever) # add escape sequences
    temp.append(enhanced + " " * npad)
sys.stdout.write("".join(temp))

Update-1

Nach OP Kommentar:

  

Der Grund, warum ich möchte, dass sie Streifen aus und berechnen die Länge nach dem   String enthält den Farbcodes ist, da alle Daten aufgebaut werden   programmatisch. Ich habe eine Reihe von kolorieren Methoden und ich baue   bis die Daten so etwas wie diese: str = "%s/%s/%s" % (GREEN(data1), BLUE(data2), RED(data3)) wäre es ziemlich schwierig sein, die zu färben   Text nach der Tat.

Wenn die Daten aus Stücken mit jeweils eigener Formatierung aufgebaut ist, können Sie immer noch die angezeigte Länge und Pad entsprechend berechnen. Hier ist eine Funktion, die macht das für eine Zelle Inhalt:

BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(40, 48)
BOLD = 1

def render_and_pad(reqd_width, components, sep="/"):
    temp = []
    actual_width = 0
    for fmt_code, text in components:
        actual_width += len(text)
        strg = "\x1b[%dm%s\x1b[m" % (fmt_code, text)
        temp.append(strg)
    if temp:
        actual_width += len(temp) - 1
    npad = reqd_width - actual_width
    assert npad >= 0
    return sep.join(temp) + " " * npad

print repr(
    render_and_pad(20, zip([BOLD, GREEN, YELLOW], ["foo", "bar", "zot"]))
    )

Wenn Sie denken, dass der Anruf von Interpunktion überfrachtet ist, können Sie etwas tun könnten, wie:

BOLD = lambda s: (1, s)
BLACK = lambda s: (40, s)
# etc
def render_and_pad(reqd_width, sep, *components):
    # etc

x = render_and_pad(20, '/', BOLD(data1), GREEN(data2), YELLOW(data3))

(2) Ich verstehe nicht, warum Sie nicht möchten, den mitgelieferte-mit-Python regulären Ausdruck Kit verwenden? Nein „Hacks“ (für jede mögliche Bedeutung von „Hacks“, dass ich mir bewusst bin) beteiligt ist:

>>> import re
>>> test = "1\x1b[a2\x1b[42b3\x1b[98;99c4\x1b[77;66;55d5"
>>> expected = "12345"
>>> # regex = re.compile(r"\x1b\[[;\d]*[A-Za-z]")
... regex = re.compile(r"""
...     \x1b     # literal ESC
...     \[       # literal [
...     [;\d]*   # zero or more digits or semicolons
...     [A-Za-z] # a letter
...     """, re.VERBOSE)
>>> print regex.findall(test)
['\x1b[a', '\x1b[42b', '\x1b[98;99c', '\x1b[77;66;55d']
>>> actual = regex.sub("", test)
>>> print repr(actual)
'12345'
>>> assert actual == expected
>>>

Update-2

Nach OP Kommentar:

  

ich immer noch lieber Paul Antwort, da es mehr prägnant

prägnanter als das, was? Ist nicht die folgende regex Lösung prägnant genug für Sie?

# === setup ===
import re
strip_ANSI_escape_sequences_sub = re.compile(r"""
    \x1b     # literal ESC
    \[       # literal [
    [;\d]*   # zero or more digits or semicolons
    [A-Za-z] # a letter
    """, re.VERBOSE).sub
def strip_ANSI_escape_sequences(s):
    return strip_ANSI_escape_sequences_sub("", s)

# === usage ===
raw_data = strip_ANSI_escape_sequences(formatted_data)

[Above Code korrigiert, nachdem @ Nick Perkins wies darauf hin, dass es nicht funktioniert hat]

Suchen Sie in ANSI_escape_code , die Sequenz in Ihrem Beispiel ist Wählen Sie Darstellungsart (wahrscheinlich fett ).

Versuchen Sie, Steuersäule Positionierung mit dem Cursorposition (CSI n ; m H) -Sequenz. Auf diese Weise Breite Text vorangegangener hat keine Auswirkungen auf aktuelle Spaltenposition und es gibt keinen Grund zur Sorge über String Breite.

Eine bessere Option, wenn Sie Unix Ziel wird mit dem Flüche Modulfenster -Objekte . Zum Beispiel kann eine Zeichenfolge auf dem Bildschirm positioniert werden, mit:

  

window.addnstr([y, x], str, n[, attr])

     

Farbe höchstens n Zeichen des Strings str bei (y, x) mit Attributen attr etwas Überschreiben zuvor auf dem Display.

Wenn Sie gerade das Hinzufügen von Farbe zu einigen Zellen, Sie 9 auf die erwartete Zellenbreite hinzufügen (5 versteckte Zeichen auf die Farbe zu drehen, 4, um sie auszuschalten), z.B.

import colorama # handle ANSI codes on Windows
colorama.init()

RED   = '\033[91m' # 5 chars
GREEN = '\033[92m' # 5 chars
RESET = '\033[0m'  # 4 chars

def red(s):
    "color a string red"
    return RED + s + RESET
def green(s):
    "color a string green"
    return GREEN + s + RESET
def redgreen(v, fmt, sign=1):
    "color a value v red or green, depending on sign of value"
    s = fmt.format(v)
    return red(s) if (v*sign)<0 else green(s)

header_format = "{:9} {:5}  {:>8}  {:10}  {:10}  {:9}  {:>8}"
row_format =    "{:9} {:5}  {:8.2f}  {:>19}  {:>19}  {:>18}  {:>17}"
print(header_format.format("Type","Trial","Epsilon","Avg Reward","Violations", "Accidents","Status"))

# some dummy data
testing = True
ntrials = 3
nsteps = 1
reward = 0.95
actions = [0,1,0,0,1]
d = {'success': True}
epsilon = 0.1

for trial in range(ntrials):
    trial_type = "Testing " if testing else "Training"
    avg_reward = redgreen(float(reward)/nsteps, "{:.2f}")
    violations = redgreen(actions[1] + actions[2], "{:d}", -1)
    accidents = redgreen(actions[3] + actions[4], "{:d}", -1)
    status = green("On time") if d['success'] else red("Late")
    print(row_format.format(trial_type, trial, epsilon, avg_reward, violations, accidents, status))

Geben

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top