在Groovy的多行字符串中去除缩进

59

很遗憾,stripIndent 在多行字符串上不起作用。 附带说明:我的IDE代码风格偏好只允许使用空格缩进(制表符会被替换为空格)。但我认为这不应该有任何影响。

def s = """ This 
            is
            multiline
"""
println s.stripIndent()

不打印

This
is
multiline

如预期所料。

输出具有缩进。

This 
               is
               multiline

这里出了什么问题?

我使用 Groovy 2.0.7 和 Eclipse Indigo SR2。

似乎使用 反斜线 \(字符串延续字符)在第一行中可以解决问题。但我不明白为什么这是必须的。

7个回答

81
你可以使用.stripIndent()来移除多行字符串的缩进,但要记住,如果没有给出任何缩进量,它将自动从包含最少前导空格的行中确定。
对于你的字符串来说,这将只在This前面有一个空格,并将从多行字符串的每一行中移除它。
def s = """ This 
            is
            multiline
"""
为了解决这个问题,您可以像下面的示例一样转义多行字符串的第一行,以获得您期望的结果:
def s = """\
           This
           is
           multiline
"""

是的,你说得对,换行符仍然存在,但缩进呢?还是有缩进的。这就是我的问题。 - Cengiz
1
这是因为你应该像我示例中所示一样转义多行字符串的第一行。否则,缩进的检测将无法获得正确的空格数量以进行删除。 - stefanglase
好的,谢谢。我不知道缩进计算是基于第一行的。 - Cengiz
5
从 stripIndent 的 groovydocs 中可以看出:将字符串中每行开头的空格删除。行首空格数量最少的行决定了要删除多少个空格。在计算需要删除的前导空格数量时,只有包含非空白字符的行才会被考虑。这很清楚地解释了它的工作原理。也就是说,这不是基于第一行,而是基于行首空格数量最少的那一行。 - Matias Bjarland

31

如果可行的话,也可以使用.stripMargin()

def s = """ This 
            | is
            | multiline
        """
println s.stripMargin().stripIndent()

6

stripMargin()

是用于去除带有边距的行前空格的函数。
默认的边距符号是|。我们也可以指定自定义的边距符号。
例如,
def s = """*This 
        *is
            *multiline
"""

println(s.stripMargin("*"))

将导致

This 
is
multiline

最佳实践是在末尾添加 .trim() 以消除整个字符串的前导和尾随空格。
例如,
println(s.stripMargin("*").trim())

5

针对其他遇到类似问题的人,stefanglase提供的解决方案确实很好,但是在包含多行字符串的断言失败时,在Spock测试中会引发MultipleCompilationErrorsException:

org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
Spec expression: 1: unexpected char: '\' @ line 1, column 16.
   myString == '''\ This Is Multiline '''.stripIndent()

我的解决方案是:

def myString = '''
  This
  Is
  Multiline
  '''.replaceFirst("\n","").stripIndent()

现在,当断言失败时,您将看到通常的差异指示出了什么出错了,而不是编译异常。

基于@protocol-x的回答和我的测试,被downvote了。 - akostadinov
@akostadinov 你好 Alex :) 对我来说可以工作。结果:'这是\n多行\n' - Ondra Žižka
@OndraŽižka,因为这不是对stefanglase答案的改进而被downvote。只是Josh的一个错误。很高兴见到你! - akostadinov

4

我有一个类似的用例,因为我想在行内格式化我的SQL查询。例如,嵌套查询:

String query = '''
    select ${CONTROL_ID} from ${TABLE_NAME} 
        where 
            location_id = ( 
                select id from location where code = ${locationCode} 
            )  
''';

用Groovy编写的代码比Java版本使用""..."+ TABLE_NAME +"..."要好得多,相信你能同意这一点。

这种类型的字符串无法使用.stripIndent()方法。相反,我保留了query中的换行符(如上所示)--请注意,行尾没有 "\"。

因此

String query = """
    select ${CONTROL_ID} from ${TABLE_NAME} 
        where 
            location_id = ( 
                select id from location where code = '${locationCode}' 
            )  
""".replaceAll( /\n\s*/, " " );

println  "  Query= ${query};"; 

提供一个格式整齐的单行SQL查询结果:

  Query = select id from controls where  location_id = (  select id from location where code = '003');

我发现这个方法非常有用并且易于阅读。我使用单个空格替换,以确保SQL保持离散。重要的部分是使用换行符(\n)作为默认的起始行或锚点。适用于混合TAB和SPACE。

当然,这对原问题也适用。


关于正则表达式,我想再提一点,那就是我要为 groovyConsole 命令做个广告。可以节省很多时间。 - will

3

正如@stefanglase所提到的,我使用.stripIndent().trim()组合使用:

String mystring = """
     My multiline
          string
     contains blank lines
           at the beginning and the end
"""
print "*${mystring.stripIndent()}*"
print "*${mystring.stripIndent().trim()}*"


>*
>My multiline
>     string
>contains blank lines
>      at the beginning and the end
>*
>*My multiline
>     string
>contains blank lines
>      at the beginning and the end*

1
如果您使用的是JDK 13+,则需要使用.stripIndent(true)在Groovy中获得此行为。 https://docs.groovy-lang.org/latest/html/groovy-jdk/java/lang/CharSequence.html#stripIndent(boolean) - ZombieDev

2
你是不是本意使用==而非=?我在使用你的示例时遇到了错误。如果我将其改回=并在没有replaceFirst()的情况下使用你的示例,那么它就可以正常工作且没有错误。
此外,在进行单行操作时,不能使用\。如果我使用'''\ This Is Multiline '''.stripIndent(),我会遇到与你完全相同的问题。

2
将此作为对@josh-feldman答案的评论点赞。用户没有足够的声望来发表评论。 - akostadinov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接