الحصول على تصحيح طول السلسلة في بيثون سلاسل مع ANSI رموز الألوان

StackOverflow https://stackoverflow.com/questions/2186919

  •  25-09-2019
  •  | 
  •  

سؤال

لدي بعض الثعبان رمز تلقائيا طباعة مجموعة من البيانات لطيفة في شكل عمود ، بما في ذلك وضع في المناسبة ASCII تسلسلات الهروب إلى اللون أجزاء مختلفة من البيانات من أجل القراءة.

أنا في نهاية المطاف مع كل سطر يمثل قائمة مع كل بند كونه العمود هو مساحة مبطن بحيث نفس الأعمدة في كل سطر دائما نفس الطول.للأسف عندما تذهب في الواقع إلى طباعة هذا ليس كل أعمدة خط.وأظن أن هذا هو أن تفعل مع ASCII تسلسلات الهروب - لأن len وظيفة لا يبدو أن ندرك هذه:

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

وذلك في حين أن كل عمود هو نفس طول وفقا len, ، فهي ليست في الواقع نفس طول عند طباعتها على الشاشة.

هل هناك أي طريقة (حفظ لفعل بعض hackery مع التعبيرات العادية التي أنا لا أفضل) اتخاذ هرب سلسلة ومعرفة ما المطبوعة طول حتى أتمكن من الفضاء لوحة مناسب ؟ ربما بعض الطريق فقط "طباعة" من العودة إلى سلسلة ودراسة طول من ذلك ؟

هل كانت مفيدة؟

المحلول

ويشمل الويكي الغامق هذا تعبير مفيد لمطابقة تسلسل ANSI Escape:

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

إليكم كيفية جعل هذا في محطة هروب تسلسل:

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)

مطبوعات:

0.0 3

نصائح أخرى

أنا لا أفهم أمرين.

(1) هو الكود الخاص بك تحت السيطرة.كنت ترغب في إضافة تسلسلات الهروب إلى البيانات الخاصة بك ثم قطاع بها مرة أخرى بحيث يمكنك حساب طول البيانات الخاصة بك??يبدو أبسط من ذلك بكثير لحساب الحشو قبل إضافة تسلسلات الهروب.ما أنا في عداد المفقودين ؟

دعونا نفترض أن أيا من تسلسلات الهروب بتغيير موضع المؤشر.إذا المقبولة حاليا الإجابة لا تعمل على أي حال.

دعونا نفترض أن لديك سلسلة البيانات في كل عمود (قبل إضافة تسلسلات الهروب) في قائمة اسمه string_data قبل تحديد الأعمدة في قائمة اسمه width.محاولة شيء من هذا القبيل:

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))

تحديث-1

بعد OP التعليق:

السبب أريد أن قطاع بها وحساب طول بعد سلسلة تحتوي على رموز الألوان هو لأن جميع البيانات المبنية برمجيا.لدي مجموعة من تلوين أساليب و أنا أبني البيانات عن شيء مثل هذا: str = "%s/%s/%s" % (GREEN(data1), BLUE(data2), RED(data3)) سيكون من الصعب جدا أن لون النص بعد وقوعها.

إذا كانت البيانات المبنية من قطع مع التنسيق, لا يزال يمكنك حساب طول و عرض الوسادة عند الاقتضاء.وهنا وظيفة التي لا يجب أن محتويات الخلية:

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"]))
    )

إذا كنت تعتقد أن هذه الدعوة هي مثقلة علامات الترقيم, هل يمكن أن تفعل شيئا مثل:

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) أنا لا أفهم لماذا كنت لا تريد استخدام الموردة مع بيثون التعبير العادي العدة ؟ لا "hackery" (أي معنى ممكن من "hackery" أنا على علم) تشارك:

>>> 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
>>>

تحديث-2

بعد OP التعليق:

أنا لا تزال تفضل إجابة بولس منذ أكثر إيجازا

أكثر إيجازا من ماذا ؟ ليس التالية regex الحل موجزة بما فيه الكفاية بالنسبة لك ؟

# === 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)

[فوق رمز تصحيح بعد @نيك بيركنز أشار إلى أنه لم يعمل]

ينظر في ANSI_ESCAPE_CODE, ، التسلسل في مثالك هوحدد عرض الرسوم (من المحتمل بالخط العريض).

حاول التحكم في تحديد موقع العمود مع موقف المؤشر ( CSI n ; m H) تسلسل. وبهذه الطريقة ، لا يؤثر عرض النص السابق على موضع العمود الحالي وليس هناك حاجة للقلق بشأن عرض السلسلة.

خيار أفضل ، إذا كنت تستهدف UNIX ، هو استخدام اللعنات الوحدة النمط النافذة. على سبيل المثال ، يمكن وضع سلسلة على الشاشة مع:

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

قم بالطلاء على معظم أحرف n من String str At (y ، x) مع سمات السمات ، والكتابة فوق أي شيء سابقًا على الشاشة.

إذا كنت فقط تضيف لونًا إلى بعض الخلايا ، فيمكنك إضافة 9 إلى عرض الخلية المتوقع (5 أحرف مخفية لتشغيل اللون ، 4 لإيقافها) ، على سبيل المثال

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))

إعطاء

screenshot

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top