如何删除Python三引号多行字符串的额外缩进?


74

我有一个python编辑器,用户在其中输入脚本或代码,然后将其放入幕后的主要方法中,同时每行缩进。问题是,如果用户具有多行字符串,则通过在每个空格中插入制表符,对整个脚本进行的缩进会影响字符串。问题脚本非常简单,例如:

"""foo
bar
foo2"""

因此,在main方法中,它看起来像:

def main():
    """foo
    bar
    foo2"""

并且该字符串现在在每一行的开头都有一个额外的制表符。


Answers:


-12

因此,如果我正确理解它,则可以接受用户输入的任何内容,对其进行适当的缩进,然后将其添加到程序的其余部分中(然后运行整个程序)。

因此,在将用户输入放入程序后,您可以运行正则表达式,基本上可以将强制缩回。诸如此类:在三引号中,将所有“换行标记”替换为四个空格(或制表符),仅用“换行标记”代替。


是的,正好。那是我提出的唯一可能的解决方案。不知道为什么我没有继续进行...我想如果没有更好的办法,我可能必须这样做。

22
@thraxil建议使用textwrap.dedent是可行的方法。考虑更改您接受的答案。
克里斯·卡洛

3
@ChrisCalo @ bbenne10的答案甚至更好
user2297550

132

标准库中的textwrap.dedent可以自动撤消古怪的缩进。


11
标准库永不停息。
thraxil

22
请注意,如果第一行以开头"""foo,则第一行缺少其他行具有的前导缩进,因此dedent不会执行任何操作。如果您等待在下一行开始foo并转义第一个换行符,它将起作用,例如:`“”“ \`
Scott H

3
要解决@ScottH提到的短消息,请查看我的答复inspect.cleandoc
bbenne10

45

从我的角度来看,这里可能是一个更好的答案inspect.cleandoc,它可以做很多事情textwrap.dedent,但也可以解决textwrap.dedent领先优势。

以下示例显示了差异:

>>> import textwrap
>>> import inspect
>>> x = """foo bar
    baz
    foobar
    foobaz
    """
>>> inspect.cleandoc(x)
'foo bar\nbaz\nfoobar\nfoobaz'
>>> textwrap.dedent(x)
'foo bar\n    baz\n    foobar\n    foobaz\n'
>>> y = """
...     foo
...     bar
... """
>>> textwrap.dedent(y)
'\nfoo\nbar\n'
>>> inspect.cleandoc(y)
'foo\nbar'
>>> z = """\tfoo
bar\tbaz
"""
>>> textwrap.dedent(z)
'\tfoo\nbar\tbaz\n'
>>> inspect.cleandoc(z)
'foo\nbar     baz'

请注意,这inspect.cleandoc还会将内部制表符扩展为空格。这可能不适合一个人的用例,但对我来说很好。


2
请注意,这两者在其他方面并不完全相同,cleandoc所做的处理不仅仅是删除缩进。至少扩展'\t'' '
Brian

1
的确如此,但当时我没有注意到。我将更新答案以至少反映选项卡的扩展。
bbenne10 '19

19

多行字符串的第一行之后的内容是字符串的一部分,解析器未将其视为缩进。您可以自由地写:

def main():
    """foo
bar
foo2"""
    pass

它会做正确的事。

另一方面,这是不可读的,Python知道这一点。因此,如果文档字符串的第二行中包含空格,则当您用于help()查看文档字符串时,该空格量会被剥离。因此,help(main)下面的代码会help(main2)产生相同的帮助信息。

def main2():
    """foo
    bar
    foo2"""
    pass

谢谢回复。不幸的是,缩进是完全自动化的,因为我的代码在脚本中以字符串的形式读取(在Java中),并对该字符串中的每一行进行缩进。

我不认为只有文档字符串会使用三引号。此自动化不会在其他地方应用
三叶草

@tribbloid docstrings的特殊逻辑特定于help()默认情况下使事情变得更好的用例。要在其他地方使用相同的确定逻辑,您可以使用textwrap.dedent()此问题的其他答案中所述的方法。
SingleNegationElimination

1

我看到的唯一方法是-从第二行开始为每行剥离前n个制表符,其中n是main方法的已知标识。

如果该标识是事先未知的-您可以在插入尾随换行符之前添加尾随换行符,并从最后一行删除标签的数量...

第三种解决方案是解析数据并找到多行引号的开头,并且不要在之后将您的标识添加到每一行,直到将其关闭为止。

认为有更好的解决方案。


谢谢回复。因此,您建议我剥离已插入的压痕的每一行?我很困惑……

0

显示之间的差别textwrap.dedentinspect.cleandoc用少许更加清晰:

没有缩进的行为

import textwrap
import inspect

string1="""String
with
no indentation
       """
string2="""String
        with
        indentation
       """
print('string1 plain=' + repr(string1))
print('string1 inspect.cleandoc=' + repr(inspect.cleandoc(string1)))
print('string1 texwrap.dedent=' + repr(textwrap.dedent(string1)))
print('string2 plain=' + repr(string2))
print('string2 inspect.cleandoc=' + repr(inspect.cleandoc(string2)))
print('string2 texwrap.dedent=' + repr(textwrap.dedent(string2)))

输出量

string1 plain='String\nwith\nno indentation\n       '
string1 inspect.cleandoc='String\nwith\nno indentation\n       '
string1 texwrap.dedent='String\nwith\nno indentation\n'
string2 plain='String\n        with\n        indentation\n       '
string2 inspect.cleandoc='String\nwith\nindentation'
string2 texwrap.dedent='String\n        with\n        indentation\n'

缩进前导部分的行为

string1="""
String
with
no indentation
       """
string2="""
        String
        with
        indentation
       """

print('string1 plain=' + repr(string1))
print('string1 inspect.cleandoc=' + repr(inspect.cleandoc(string1)))
print('string1 texwrap.dedent=' + repr(textwrap.dedent(string1)))
print('string2 plain=' + repr(string2))
print('string2 inspect.cleandoc=' + repr(inspect.cleandoc(string2)))
print('string2 texwrap.dedent=' + repr(textwrap.dedent(string2)))

输出量

string1 plain='\nString\nwith\nno indentation\n       '
string1 inspect.cleandoc='String\nwith\nno indentation\n       '
string1 texwrap.dedent='\nString\nwith\nno indentation\n'
string2 plain='\n        String\n        with\n        indentation\n       '
string2 inspect.cleandoc='String\nwith\nindentation'
string2 texwrap.dedent='\nString\nwith\nindentation\n'
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.