【问题标题】:How the intents processed in a Text block(Java 13)如何在文本块中处理意图(Java 13)
【发布时间】:2019-09-20 14:34:06
【问题描述】:

我刚刚尝试了 Java 13 中的新文本块功能,遇到了一个小问题。

我已阅读this article from Jaxcenter

右三引号会影响格式。

String query = """
            select firstName,
            lastName,
            email
            from User
            where id= ?
        """;

System.out.println("SQL or JPL like query string :\n" + query);

上述格式效果很好。为了与结束分隔符 (""") 对齐,多行字符串在每行之前都留有空格。

但是当我尝试比较以下两个文本块字符串时,它们在输出控制台中的格式相同,但它们不等于,即使在stripIntent 之后也是如此。

String hello = """
    Hello,
    Java 13
    """;

String hello2 = """
    Hello,
    Java 13
""";

System.out.println("Hello1:\n" + hello);
System.out.println("Hello2:\n" + hello);

System.out.println("hello is equals hello2:" + hello.equals(hello2));

System.out.println("hello is equals hello2 after stripIndent():" + hello.stripIndent().equals(hello2.stripIndent()));

输出控制台是这样的:

hello is equals hello2:false
hello is equals hello2 after stripIndent():false

我不确定哪里错了,或者这是文本块设计的目的?

更新:只需打印 hello2 stripIntent,

System.out.println("hello2 after stripIntent():\n" + hello2.stripIndent());

stripIntent 按预期删除每行前的空格。

更新:在阅读了相关的java文档后,我认为在文本块编译后,它应该已经剥离了块中行的左意图。 stripIntent 用于文本块的目的是什么?我知道在普通字符串上使用它很容易理解。

完整代码为here

【问题讨论】:

  • 文章指出“在第二步中,由于代码格式的原因,空格被删除。它被标记为结束三个引号的位置。这允许您在代码中放置文本块以便它匹配其余的代码格式”。这让我相信,由于结束 """ 的位置不同,空格存在差异。您是否对此进行了调试并查看了运行时的变量?
  • 只打印 stripIntent 结果,hello2 中每行之前的空格不会按预期删除。
  • 我不明白这个问题,两个字符串是不同的(hello 删除了 4 个空格;hello2 没有);而且,因为有一个额外的(空)行,stripIndent() 什么都不做(我相信它已经在 input 行上使用了 - 我希望 hello.stripIndent().equals(hello)hello2 相同)
  • @CarlosHeuberger 我有点困惑 stripIntent 如何在文本块上工作,line#89 它在正常字符串中按预期工作。
  • 阅读它的documentation,其实就是对齐文本块的方法——就是文本块中文本缩进的逻辑(总结一下 行开头,并从 每个 行开头删除该数量的空格)

标签: java string jls java-13


【解决方案1】:

有一个偶然空白的概念。

JEP 355: Text Blocks (Preview)

编译时处理

文本块是字符串类型的常量表达式,就像字符串文字一样。但是,与字符串文字不同,文本块的内容由 Java 编译器分三个不同的步骤处理:

  • 内容中的行终止符被转换为 LF (\u000A)。这种翻译的目的是在跨平台移动 Java 源代码时遵循最小意外原则。

  • 为匹配 Java 源代码的缩进而引入的围绕内容的附带空白已被移除。

  • 内容中的转义序列被解释。将解释作为最后一步意味着开发人员可以编写转义序列,例如 \n,而不会被前面的步骤修改或删除。

...

附带的空白

这是一个使用点来可视化空间的 HTML 示例 为缩进添加了开发人员:

String html = """
..............<html>
..............    <body>
..............        <p>Hello, world</p>
..............    </body>
..............</html>
..............""";

由于开始分隔符通常定位在与语句相同的行或 消耗文本块的表达式,没有真正的 14 个可视化空间从每行开始这一事实的意义。 在内容中包含这些空格将意味着文本块 表示一个与连接的字符串不同的字符串 字符串文字。这会损害移民,并成为经常性的来源 令人惊讶的是:开发人员极有可能不会 想要字符串中的那些空格。此外,结束分隔符是 通常定位与内容对齐,这进一步表明 14 个可视化空间微不足道。
...
因此,对文本块内容的适当解释是将每行开头和结尾的附带空白与基本空白区分开来。Java 编译器通过删除附带空白来处理内容产生开发人员的意图。

你的假设

    Hello,
    Java 13
<empty line>

等于

....Hello,
....Java 13
<empty line>

不准确,因为它们是基本空格,编译器或String#stripIndent都不会删除它们。

为了清楚起见,让我们继续将偶然的空白表示为一个点。

String hello = """
....Hello,
....Java 13
....""";

String hello2 = """
    Hello,
    Java 13
""";

让我们打印出来。

Hello,
Java 13
<empty line>

    Hello,
    Java 13
<empty line>

让我们同时调用String#stripIndent 并打印结果。

Hello,
Java 13
<empty line>

    Hello,
    Java 13
<empty line>

要了解为什么没有任何变化,我们需要查看文档。

String#stripIndent

返回一个字符串,其值为该字符串,并从每行的开头和结尾删除偶然的空白

然后,最小压痕(min)如下确定。对于每个非空白行(由isBlank() 定义),都会计算前导空白字符。 最后一行的前导空白字符即使是空白也被计算在内。最小值是这些计数中的最小值。

对于每个非空行,删除最少的前导空白字符,并删除任何尾随空白字符。空行替换为空字符串。

对于Strings,最小缩进为0

Hello,          // 0
Java 13         // 0    min(0, 0, 0) = 0 
<empty line>    // 0

    Hello,      // 4
    Java 13     // 4    min(4, 4, 0) = 0
<empty line>    // 0

String#stripIndent 使开发人员可以访问编译器使用的重新缩进算法的 Java 版本。

JEP 355

重新缩进算法将在 Java 语言规范中成为规范。开发者可以通过String::stripIndent 访问它,这是一种新的实例方法。

Specification for JEP 355

文本块表示的字符串不是内容中字符的文字序列。相反,由文本块表示的字符串是按顺序对内容应用以下转换的结果:

  1. 行终止符被规范化为 ASCII LF 字符 (...)

  2. 删除了附带的空白,就像对内容中的字符执行String::stripIndent一样。

  3. 转义序列被解释为字符串文字。

【讨论】:

    【解决方案2】:

    TLDR。您的示例字符串不相等,Java 告诉您它们不相等是正确的。

    考虑阅读String.stripIndent 方法的描述。 以下是来自 jaxenter.com 帖子的释义:

    stripIndent 方法删除所有行共有的多行字符串前面的空格,即将整个文本向左移动而不更改格式。

    注意“所有行都有共同点”这句话。

    现在,将“that all lines have common”应用于以下文字字符串:

    String hello2 = """
        Hello,
        First, notice that the final line of this example has zero spaces.
        Next, notice that all other lines of this example have non-zero spaces.
    """; // <--- This is a line in the text block.
    

    关键是“0 != 3”。

    【讨论】:

      【解决方案3】:

      使用jshell 进行测试:

      String hello = """
          Hello,
          Java 13
          """;
      hello.replace(" ", ".");
      

      结果

      "Hello\nJava13\n"
      

      注意:没有空格

      String hello2 = """
          Hello,
          Java 13
      """;
      hello2.replace(" ", ".");
      

      结果

      "....Hello\n....Java13\n"
      

      请注意,两个结果在最后一行中没有空格,在最后一个 \n 之后,所以 stripIndent() 不会删除任何空格


      stripIndent() 的作用与编译器对文本块的作用相同。示例

      String hello3 = ""
          + "    Hello\n"
          + "    Java13\n"
          + "  ";
      hello3.stripIndent().replace(" ", ".");
      

      结果

      "..Hello\n..Java13\n"
      

      即从所有 3 行中删除两个空格;两个空格,因为最后一行有2个空格(其他行有更多,所以最多可以从所有行中删除2个空格)

      【讨论】:

        猜你喜欢
        • 2017-07-26
        • 1970-01-01
        • 2017-11-30
        • 2019-12-04
        • 2020-08-25
        • 1970-01-01
        • 2016-03-08
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多