2010-02-02 64 views
16

Tôi có một số mã Python sẽ tự động in một bộ dữ liệu theo định dạng cột đẹp, bao gồm việc đặt chuỗi thoát ASCII thích hợp để tô màu các phần khác nhau của dữ liệu cho dễ đọc.Lấy chuỗi dài đúng bằng Python cho chuỗi có mã màu ANSI

Cuối cùng tôi kết thúc với mỗi dòng được thể hiện dưới dạng danh sách, với mỗi mục là một cột được đệm không gian sao cho các cột giống nhau trên mỗi dòng luôn có cùng độ dài. Thật không may khi tôi thực sự đi để in này, không phải tất cả các cột xếp hàng. Tôi nghi ngờ điều này là để làm với các trình tự thoát ASCII - vì len chức năng dường như không nhận ra những:

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

Và như vậy trong khi mỗi cột là chiều dài tương tự theo len, họ không thực sự giống nhau độ dài khi in trên màn hình.

Có cách nào (tiết kiệm để thực hiện một số hackery với cụm từ thông dụng mà tôi không muốn làm) để lấy chuỗi thoát ra và tìm hiểu chiều dài in là để tôi có thể không gian pad một cách thích hợp? Có lẽ một số cách để chỉ "in" nó trở lại chuỗi và kiểm tra chiều dài của điều đó?

+3

Đây thực sự là "ANSI" mã màu, không phải "ASCII", như sẽ được hiển thị trên thiết bị đầu cuối màu ANSI hoặc trên PC bằng trình điều khiển ANSI.SYS. – PaulMcG

Trả lời

9

Các wiki pyparsing bao gồm helpful expression này cho phù hợp về trình tự thoát ANSI:

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

Dưới đây là cách thực hiện điều này vào một lối thoát-chuỗi-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) 

in:

0.0 3 
+0

Về mặt kỹ thuật, danh sách được phân cách có thể có các chuỗi trong đó, mặc dù bạn không chắc sẽ gặp chuỗi như vậy. Xem thêm http://stackoverflow.com/questions/1833873/python-regex-escape-characters/1834669#1834669 – bobince

+1

OH, tôi không biết! Trong tuổi trẻ của tôi, chúng tôi đã thực hiện những điệu nhảy của VT100, nhấp nháy đèn LED của họ, thay đổi khu vực cuộn của họ, xuất ra các phông chữ đôi cao gấp đôi, in đậm video ngược lại - ah, những ngày khó chịu ... – PaulMcG

+0

Cảm ơn, điều đó đã làm việc hoàn hảo ! Tôi đã hy vọng chỉ có một số phương pháp blahlibrary.unescape() một nơi nào đó tôi đã nhìn, nhưng đây là điều tốt nhất tiếp theo! –

1

Tìm kiếm trong ANSI_escape_code, chuỗi trong examp của bạn le là Chọn Hiển thị đồ họa (có thể là đậm).

Cố gắng kiểm soát vị trí cột với trình tự CUrsor Position (CSI n ; m H). Bằng cách này, chiều rộng của văn bản trước đó không ảnh hưởng đến vị trí cột hiện tại và không cần phải lo lắng về độ rộng chuỗi.

Một tùy chọn tốt hơn, nếu bạn nhắm mục tiêu đến Unix, đang sử dụng curses module window-objects. Ví dụ, một chuỗi có thể được bố trí trên màn hình với:

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

Sơn tại hầu hết các nhân vật n của chuỗi str tại (y, x) với các thuộc tính attr, ghi đè lên bất cứ điều gì trước đó trên màn hình .

+0

Cảm ơn - Tôi sẽ xem xét các lời nguyền. –

3

Tôi không hiểu HAI điều.

(1) Đó là mã của bạn, dưới sự kiểm soát của bạn. Bạn muốn thêm chuỗi thoát vào dữ liệu của bạn và sau đó tách chúng ra một lần nữa để bạn có thể tính toán độ dài của dữ liệu của bạn ?? Dường như đơn giản hơn để tính toán phần đệm trước khi thêm chuỗi thoát. Tôi đang thiếu gì?

Giả sử không có trình tự thoát nào thay đổi vị trí con trỏ. Nếu có, câu trả lời hiện được chấp nhận sẽ không hoạt động.Hãy giả sử rằng bạn có dữ liệu chuỗi cho mỗi cột (trước khi thêm chuỗi thoát) trong danh sách có tên string_data và độ rộng cột được xác định trước nằm trong danh sách có tên là width. Hãy thử một cái gì đó như thế này:

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

Cập nhật sau khi bình luận OP của "" "Lý do tôi muốn lột chúng ra và tính toán chiều dài sau chuỗi chứa mã màu là bởi vì tất cả các dữ liệu được xây dựng theo chương trình. Tôi có một loạt các phương pháp colorize và tôi đang xây dựng dữ liệu như thế này: str = "% s /% s /% s"% (GREEN (dữ liệu 1), BLUE (dữ liệu 2), RED (data3)) khó khăn để tô màu văn bản sau khi thực tế. "" "

Nếu dữ liệu được tạo thành từng phần với định dạng riêng, bạn vẫn có thể tính toán độ dài và pad được hiển thị nếu thích hợp. Dưới đây là một chức năng mà nào đó cho nội dung một tế bào của:

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

Nếu bạn nghĩ rằng cuộc gọi đang quá tải bởi dấu chấm câu, bạn có thể làm một cái gì đó như:

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) Tôi không hiểu tại sao bạn không muốn sử dụng bộ biểu thức chính quy được cung cấp kèm theo Python. Không "hackery" (đối với bất kỳ ý nghĩa có thể của "hackery" mà tôi biết) có liên quan đến:

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

Cập nhật sau khi bình luận OP của "" "Tôi vẫn thích câu trả lời của Paul vì nó ngắn gọn hơn"" "

Cụ thể hơn là gì? Không phải giải pháp regex đủ ngắn gọn cho bạn:

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

??

[Trên đang điều chỉnh sau khi @Nick Perkins chỉ ra rằng nó đã không làm việc]

+0

Cảm ơn câu trả lời John. Lý do tôi muốn loại bỏ chúng ra và tính toán độ dài * sau * chuỗi chứa mã màu là bởi vì tất cả dữ liệu được xây dựng theo lập trình. Tôi có một loạt các phương pháp tô màu và tôi đang tạo dữ liệu như sau: str = "% s /% s /% s"% (GREEN (dữ liệu 1), BLUE (dữ liệu2), RED (dữ liệu3)) Thật khó để tô màu văn bản sau khi thực tế.Đối với hackery, có lẽ những gì tôi nên nói là, "Tôi tưởng tượng đây là một vấn đề giải quyết và tôi chỉ không thể tìm thấy các thư viện đúng". Đoán không, nhưng tôi vẫn thích câu trả lời của Paul vì nó ngắn gọn hơn. –

+0

OK, tôi sẽ nói về điều này. Tôi thấy những gì bạn đang nhận được với tính toán chiều dài như bạn đi. Giải pháp bạn đề xuất không hoàn toàn hiệu quả, chỉ vì biết chiều dài cột mong muốn trước thời hạn yêu cầu bạn biết độ dài của chuỗi lớn nhất được lưu trữ trong một ô của cột đó. Tuy nhiên, tôi không nghĩ rằng nó sẽ là quá khó để viết một cái gì đó mà có xung quanh điều này và không yêu cầu tước các chuỗi màu sắc ra sau khi thực tế. –

+1

Đối với nhận xét regex, tôi không gặp bất kỳ vấn đề nào khi sử dụng hỗ trợ được xây dựng của Python. Tôi chỉ thường có xu hướng nhút nhát đi làm phân tích cú pháp regex bởi vì nó dễ dàng để mess nó lên và quên một số trường hợp cạnh. Xem danh sách câu hỏi bất tận ở đây trên SO từ những người đang cố gắng sử dụng regexes với HTML để chứng minh điều đó. Hoặc, chỉ cần nhìn vào nhận xét về bài viết của Paul, trong đó chỉ ra rằng những gì ông cung cấp không thực sự chiếm các mã kiểm soát không màu. Điều đó nói rằng, khi chỉ lo lắng về màu sắc, nó khá đơn giản như bạn đã thể hiện. –

0

Nếu bạn chỉ cần bổ sung thêm màu sắc cho một số tế bào, nó là đơn giản nhất để chỉ cần thêm 9 đến chiều rộng di động dự kiến ​​(5 ẩn ký tự để bật màu, 4 để tắt nó), ví dụ

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): 
    return RED + s + RESET 
def green(s): 
    return GREEN + s + RESET 
def redgreen(v, fmt, sign=1): 
    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") 
# loop 
    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) 
Các vấn đề liên quan