2012-06-22 39 views
32

Decir que tengo la cadena¿Tiene Python una función incorporada para quitar la sangría de una cadena multilínea?

s = """ 
    Controller = require 'controller' 

    class foo 
     view: 'baz' 
     class: 'bar' 

     constructor: -> 
      Controller.mix @ 
""" 

Cada línea de la cadena tiene ahora una muesca 4 espacio global. Si esta cadena fuera declarada dentro de una función, tendría una indentación global de 8 espacios, etc.

¿Tiene Python una función para eliminar la sangría izquierda global de la cadena?

me gustaría que la salida de la función a ser:

Controller = require 'controller' 

class foo 
    view: 'baz' 
    class: 'bar' 

    constructor: -> 
     Controller.mix @" 

Respuesta

61
No

una función incorporada, sino una función de la biblioteca estándar: textwrap.dedent()

>>> print(textwrap.dedent(s)) 

Controller = require 'controller' 

class foo 
    view: 'baz' 
    class: 'bar' 

    constructor: -> 
     Controller.mix @ 
+0

No sabía que existía uno. Cosas útiles. +1. –

+0

Esa es la acumulación más rápida de votos positivos que he visto para una respuesta – Hubro

+1

Código fuente de dedent aquí: http://hg.python.org/cpython/file/2.7/Lib/textwrap.py – Jiri

5

textwrap.dedent() está cerca de lo que quiere , pero no implementa lo que pediste, porque tiene una nueva línea principal. Usted puede envolver dedent en una función que elimina el salto de línea que va desde s:

def my_dedent(string): 
    if string and string[0] == '\n': 
     string = string[1:] 
    return textwrap.dedent(string) 

Sin embargo textwrap.dedent() maneja líneas con solo espacio en blanco de manera especial que está bien si está generando fuente de Python de una declaración de varias líneas guión, donde se arrastra el espacio en blanco es insignificante.

Pero, en general, no es apropiado que textwrap.dedent() elimina los espacios en blanco extra de líneas con más espacios en blanco que el 'guión máximo', elimina los espacios en blanco de todas las líneas de espacio en blanco y que descards cualquier espacio en blanco antes del cierre """, sobre todo porque este comportamiento es undocumented and done with non-transparent regular expressions .

Como también genero código fuente que no es de Python donde los espacios son a menudo significativos, utilizo la siguiente rutina. No maneja la sangría TAB, pero le proporciona la salida que solicitó sin encabezar la nueva línea, donde textwrap.dedent() falla.

def remove_leading_spaces(s, strict=False): 
    '''Remove the maximum common spaces from all non-empty lines in string 

Typically used to remove leading spaces from all non-empty lines in a 
multiline string, preserving all extra spaces. 
A leading newline (when not useing '"""\') is removed unless the strict 
argument is True. 

Note that if you want two spaces on the last line of the return value 
without a newline, you have to use the max indentation + 2 spaces before 
the closing """. If you just input 2 spaces that is likely to be the 
maximum indent. 
    ''' 
    if s and not strict and s[0] == '\n': 
     s = s[1:] 
    lines = s.splitlines(True) # keep ends 
    max_spaces = -1 
    for line in lines: 
     if line != '\n': 
      for idx, c in enumerate(line[:max_spaces]): 
       if not c == ' ': 
        break 
      max_spaces = idx + 1 
    return ''.join([l if l == '\n' else l[max_spaces-1:] for l in lines]) 
+0

OP no preguntó por sin necesidad de quitar la nueva línea ni la eliminación de la misma. La cura más simple para la línea nueva principal es simplemente la barra inversa después de la comilla triple inicial. (Como ya lo señaló @firegurafiku). Puede haber razones para reemplazar la función textwrap.dedent() con una función personalizada, pero La eliminación de la nueva línea, que puede evitarse con una barra diagonal inversa, no parece ser una buena. – gwideman

+0

La eliminación ** programática ** de la nueva línea inicial es necesaria para obtener de la entrada OP proporcionada a la salida solicitada. s diferentes formas de manejar eso, como la reescritura de la cadena en una línea con barras invertidas explícitas. Pero el OP no preguntó cómo modificar su aporte como parece sugerir. – Anthon

+0

Parece que la tarea de OP no es procesar cadenas en general, sino cómo escribir un literal de cadena multilínea con su fuente sangrada para que coincida con la sangría de origen circundante, pero que la cadena resultante no contenga esa sangría.La pregunta específica de OP era: "¿Tiene Python una función para eliminar la sangría izquierda global de la cadena?". El problema principal de la nueva línea es relevante, pero no es necesario que lo elimine la función solicitada. Dicho esto, sus notas sobre el dedent que quita el espacio en blanco adicional en algunas situaciones es un poco alarmante, y sería interesante saber más acerca de. – gwideman

Cuestiones relacionadas