3.12 Changing the Indentation of a Multiline String
Credit: Tom Good
3.12.1 Problem
You have a string made up of
multiple lines, and you need to build another string from it, adding
or removing leading spaces on each line so that the indentation of
each line is some absolute number of spaces.
3.12.2 Solution
We
don't need re for this. The
string module (or string methods, in Python 2.0
and later) is quite sufficient:
import string
def reindent(s, numSpaces):
s = string.split(s, '\n')
s = [(numSpaces * ' ') + string.lstrip(line) for line in s]
s = string.join(s, '\n')
return s
3.12.3 Discussion
When working with text, it may be necessary to change the indentation
level of a block. This recipe's code takes a
multiline string and adds or removes leading spaces on each line so
that the indentation level of each line of the block matches some
absolute number of spaces. For example:
>>> x = """line one
... line two
... and line three
... """
>>> print x
line one
line two
and line three
>>> print reindent(x, 8)
line one
line two
and line three
Even if the lines in s are initially indented
differently, this recipe makes their indentation homogeneous. This is
sometimes what we want, and sometimes not. A frequent need is to
adjust the amount of leading spaces in each line, so that the
relative indentation of each line in the block is preserved. This is
not hard either, for either positive or negative values of the
adjustment. However, negative values need a check to ensure that no
nonspaces are snipped from the start of the lines. Thus, we may as
well split the functionality into two functions to perform the
transformations, plus one to measure the number of leading spaces of
each line and return the result as a
list:
def addSpaces(s, numAdd):
white = " "*numAdd
return white + white.join(s.splitlines(1))
def delSpaces(s, numDel):
def aux(line, numDel=numDel, white=" "*numDel):
if line[:numDel] != white:
raise ValueError, "removing more spaces than there are!"
return line[numDel:]
return ''.join(map(aux, s.splitlines(1)))
def numSpaces(s):
return [len(line)-len(line.lstrip()) for line in s.splitlines( )]
This alternative approach relies on the string method
splitlines
(and so requires Python 2.0 or later, like any other recipe using
string methods and/or list comprehensions), which is similar to a
split on '\n', with the extra ability to leave the
trailing newline on each line when called with a true argument. This
is not often crucial (the last statement in
delSpaces, for example, might just as easily
return '\n'.join(map(aux, s.split('\n')))), but
sometimes it turns out to be (addSpaces could not
be quite as short and sweet without this ability of the
splitlines string method).
For example, here's how we can combine these
functions to build another function that deletes enough leading
spaces from each line to ensure that the least-indented line of the
block becomes flush-left, while preserving the relative indentations
of all the other lines:
def unIndentBlock(s):
return delSpaces(s, min(numSpaces(s)))
3.12.4 See Also
The Library Reference section on sequence types.
|