una separar reemplazar por palabra letras funcion eliminar contar comparar caracteres caracter cadenas cadena python escaping ansi-colors

separar - Obtención de la longitud de cadena correcta en Python para cadenas con códigos de color ANSI



separar palabra en letras python (4)

El wiki de pyparsing incluye esta expresión útil para hacer coincidir en secuencias de escape ANSI:

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

Aquí es cómo hacer esto en una secuencia de escape-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)

huellas dactilares:

0.0 3

Tengo un código Python que imprimirá automáticamente un conjunto de datos en un agradable formato de columna, que incluye poner las secuencias de escape ASCII apropiadas para colorear varias partes de los datos para facilitar la lectura.

Finalmente, termino con cada línea representada como una lista, y cada elemento es una columna que se rellena con espacios para que las mismas columnas en cada línea tengan siempre la misma longitud. Desafortunadamente, cuando realmente voy a imprimir esto, no todas las columnas se alinean. Sospecho que esto tiene que ver con las secuencias de escape ASCII, porque la función len no parece reconocer estas:

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

Y así, mientras que cada columna tiene la misma longitud de acuerdo con len , en realidad no tienen la misma longitud cuando se imprimen en la pantalla.

¿Hay alguna forma (excepto por hacer una piratería con expresiones regulares que preferiría no hacerlo) tomar la cadena escapada y averiguar cuál es la longitud impresa para poder espaciar la almohadilla apropiadamente? ¿Tal vez alguna forma de simplemente "imprimirlo" de nuevo en una cuerda y examinar la longitud de eso?


Mirando en ANSI_escape_code , la secuencia en su ejemplo es Seleccionar Rendimiento Gráfico (probablemente negrita ).

Intente controlar la posición de la columna con la secuencia de posición CUrsor ( CSI n ; m H ). De esta manera, el ancho del texto anterior no afecta la posición actual de la columna y no hay necesidad de preocuparse por los anchos de cadena.

Una mejor opción, si apuntas a Unix, es usar los objetos de ventana del módulo curses . Por ejemplo, una cadena se puede colocar en la pantalla con:

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

Pinte a lo sumo n caracteres de la cadena str en (y, x) con atributos attr, sobrescribiendo cualquier cosa que haya previamente en la pantalla.


No entiendo DOS cosas.

(1) Es tu código, bajo tu control. ¿Desea agregar secuencias de escape a sus datos y luego eliminarlas de nuevo para poder calcular la longitud de sus datos? Parece mucho más sencillo calcular el relleno antes de agregar las secuencias de escape. ¿Qué me estoy perdiendo?

Supongamos que ninguna de las secuencias de escape cambia la posición del cursor. Si lo hacen, la respuesta actualmente aceptada no funcionará de todos modos.

Supongamos que tiene los datos de cadena para cada columna (antes de agregar secuencias de escape) en una lista llamada string_data y los anchos de columna predeterminados están en una lista llamada width . Intenta algo como esto:

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

Actualización-1

Después del comentario de OP:

La razón por la que quiero eliminarlos y calcular la longitud después de que la cadena contenga los códigos de color es porque todos los datos se crean mediante programación. Tengo un montón de métodos de colorear y estoy acumulando los datos algo como esto: str = "%s/%s/%s" % (GREEN(data1), BLUE(data2), RED(data3)) Sería bastante difícil colorear el texto después del hecho.

Si los datos están compuestos de piezas, cada una con su propio formato, aún puede calcular la longitud y el pad que se muestran, según corresponda. Aquí hay una función que hace eso para el contenido de una celda:

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

Si cree que la puntuación está sobrecargada por la puntuación, podría hacer algo como:

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) No entiendo por qué no quiere usar el kit de expresión regular suministrado con Python. No hay "piratería" (para cualquier significado posible de "piratería" que yo sepa) está involucrado:

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

Actualización-2

Después del comentario de OP:

Sigo prefiriendo la respuesta de Paul porque es más concisa.

Más conciso que qué? ¿No es la siguiente solución de regex lo suficientemente concisa para usted?

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

[El código de arriba se corrigió después de que @Nick Perkins señaló que no funcionó]


Si solo está agregando color a algunas celdas, puede agregar 9 al ancho de celda esperado (5 caracteres ocultos para activar el color, 4 para desactivarlo), por ejemplo

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

Dando