【问题标题】:Does Java have support for multiline strings?Java 是否支持多行字符串?
【发布时间】:2009-05-18 16:28:47
【问题描述】:

来自 Perl,我肯定错过了在源代码中创建多行字符串的“here-document”方法:

$string = <<"EOF"  # create a three-line string
text
text
text
EOF

在 Java 中,当我从头开始连接多行字符串时,我必须在每一行加上繁琐的引号和加号。

有哪些更好的选择?在属性文件中定义我的字符串?

编辑:两个答案说 StringBuilder.append() 比加号更可取。谁能详细说明他们为什么这么认为?它看起来一点也不适合我。我正在寻找解决多行字符串不是一流语言结构这一事实的方法,这意味着我绝对不想用方法调用替换一流的语言结构(字符串与加号连接)。

编辑:为了进一步澄清我的问题,我根本不关心性能。我担心可维护性和设计问题。

【问题讨论】:

  • StringBuilder.append() 在重复添加到字符串时比 plus 更可取,因为每次您执行 string1 + string2 时,您都在分配一个新的字符串对象并从两个输入字符串中复制字符。如果您将 n 个字符串加在一起,您将进行 n-1 次分配和大约 (n^2)/2 个字符副本。另一方面,StringBuilder 复制和重新分配的频率较低(尽管当您超过其内部缓冲区的大小时,它仍然会同时执行这两种操作)。理论上,有些情况下编译器可以将 + 转换为使用 StringBuilder,但实际上谁知道呢。
  • 每次我跳入调试器时,+ 都会在 Java 1.5 上转换为 StringBuilder.append() 调用。我有同事困惑地告诉我 StringBuilder 有一个错误,因为他们调试到似乎没有调用它的代码并在那里结束。
  • 请注意,由 "abc\n" + "def\n" 等组成的字符串文字不使用 StringBuilder:编译器将它们粘合在一起并将它们作为单个放入 .class 文件中字面量,与其他类型的常量折叠相同。
  • 大多数 IDE 支持输入多行字符串。 IE。您只需将所需内容键入或粘贴到“”字符串中,它就会根据需要添加 \n 和“+”。例如我可以将 40 行文本粘贴到字符串中,然后 IDE 会为您整理出来。

标签: java string multiline


【解决方案1】:

当使用一长串的 + 时,只会创建一个 StringBuilder,除非在编译时确定了 String,在这种情况下不使用 StringBuilder!

StringBuilder 唯一效率更高的时候是使用多个语句来构造 String。

String a = "a\n";
String b = "b\n";
String c = "c\n";
String d = "d\n";

String abcd = a + b + c + d;
System.out.println(abcd);

String abcd2 = "a\n" +
        "b\n" +
        "c\n" +
        "d\n";
System.out.println(abcd2);

注意:仅创建一个 StringBuilder。

  Code:
   0:   ldc     #2; //String a\n
   2:   astore_1
   3:   ldc     #3; //String b\n
   5:   astore_2
   6:   ldc     #4; //String c\n
   8:   astore_3
   9:   ldc     #5; //String d\n
   11:  astore  4
   13:  new     #6; //class java/lang/StringBuilder
   16:  dup
   17:  invokespecial   #7; //Method java/lang/StringBuilder."<init>":()V
   20:  aload_1
   21:  invokevirtual   #8; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   24:  aload_2
   25:  invokevirtual   #8; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   28:  aload_3
   29:  invokevirtual   #8; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   32:  aload   4
   34:  invokevirtual   #8; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   37:  invokevirtual   #9; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   40:  astore  5
   42:  getstatic       #10; //Field java/lang/System.out:Ljava/io/PrintStream;
   45:  aload   5
   47:  invokevirtual   #11; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   50:  ldc     #12; //String a\nb\nc\nd\n
   52:  astore  6
   54:  getstatic       #10; //Field java/lang/System.out:Ljava/io/PrintStream;
   57:  aload   6
   59:  invokevirtual   #11; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   62:  return

为了进一步澄清我的问题,我根本不关心性能。我担心可维护性和设计问题。

让它尽可能清晰和简单。

【讨论】:

    【解决方案2】:

    一个小技巧。使用这个我在动态创建的 HTML 页面中注入 javascritp

    StringBuilder builder = new StringBuilder();
    
    public String getString()
    {
        return builder.toString();
    }
    private DropdownContent _(String a)
    {
        builder.append(a);
        return this;
    }
    
    public String funct_showhide()
    {
       return
        _("function slidedown_showHide(boxId)").
        _("{").
        _("if(!slidedown_direction[boxId])slidedown_direction[boxId] = 1;").
        _("if(!slideDownInitHeight[boxId])slideDownInitHeight[boxId] = 0;").
        _("if(slideDownInitHeight[boxId]==0)slidedown_direction[boxId]=slidedownSpeed; ").
        _("else slidedown_direction[boxId] = slidedownSpeed*-1;").
        _("slidedownContentBox = document.getElementById(boxId);").
        _("var subDivs = slidedownContentBox.getElementsByTagName('DIV');").
        _("for(var no=0;no<subDivs.length;no++){").
        _(" if(subDivs[no].className=='dhtmlgoodies_content')slidedownContent = subDivs[no];").
        _("}").
        _("contentHeight = slidedownContent.offsetHeight;").
        _("slidedownContentBox.style.visibility='visible';").
        _("slidedownActive = true;").
        _("slidedown_showHide_start(slidedownContentBox,slidedownContent);").
        _("}").getString();
    
    }
    

    【讨论】:

    • 一个糟糕的选择。使用加号。其他评分较高的答案解释了原因和方式。
    【解决方案3】:

    后期模型 JAVA 对 + 进行了优化,带有常量字符串,在幕后使用了 StringBuffer,所以你不想用它弄乱你的代码。

    它指向一个 JAVA 疏忽,它在双引号字符串的自动连接中与 ANSI C 不同,它们之间只有空格,例如:

    const char usage = "\n"
    "Usage: xxxx <options>\n"
    "\n"
    "Removes your options as designated by the required parameter <options>,\n"
    "which must be one of the following strings:\n"
    "  love\n"
    "  sex\n"
    "  drugs\n"
    "  rockandroll\n"
    "\n" ;
    

    我希望有一个多行字符数组常量,其中嵌入的换行符得到尊重,这样我就可以毫无杂乱地呈现块,例如:

    String Query = "
    SELECT
        some_column,
        another column
      FROM
          one_table a
        JOIN
          another_table b
        ON    a.id = b.id
          AND a.role_code = b.role_code
      WHERE a.dept = 'sales'
        AND b.sales_quote > 1000
      Order BY 1, 2
    " ;
    

    要做到这一点,需要击败 JAVA 大神。

    【讨论】:

      【解决方案4】:

      这看起来有点疯狂,但由于 heredocs 是单行声明的语法糖,换行符已转义,因此可以为 Java 文件编写预处理器,在预处理期间将 heredocs 更改为单行。

      这需要在编译阶段(用于 ant/maven 构建)和 IDE 插件之前编写适当的插件来预处理文件。

      从意识形态的角度来看,它与 f.g. 没有什么不同。 “泛型”,也就是一种预处理过的语法糖。

      不过,工作量很大,所以我会在你的地方只使用 .properties 文件。

      【讨论】:

      • 有点疯狂,但我喜欢……可以演变成一种适用于 Java 的 CoffeeScript
      【解决方案5】:

      Java15 现在支持像 Python 一样的三引号字符串。

      【讨论】:

        【解决方案6】:

        我知道的唯一方法是用加号连接多行

        【讨论】:

          【解决方案7】:

          我看到至少有一种情况应该避免将外部文件用于长字符串:如果这些长字符串是单元测试文件中的预期值,因为我认为测试应该始终以它们的方式编写不要依赖任何外部资源。

          【讨论】:

          • 这太明显了——但这么多反应反映人们不理解它。
          【解决方案8】:

          我有时会使用一个并行的 groovy 类来充当字符串包

          这里的java类

          public class Test {
              public static void main(String[] args) {
                  System.out.println(TestStrings.json1);
                  // consume .. parse json
              }
          }
          

          TestStrings.groovy 中令人垂涎的多行字符串

          class TestStrings {
              public static String json1 = """
              {
                  "name": "Fakeer's Json",
                  "age":100,
                  "messages":["msg 1","msg 2","msg 3"]
              }""";
          }
          

          当然,这仅适用于静态字符串。如果我必须在文本中插入变量,我只需将整个文件更改为 groovy。只要保持强类型的做法,它就可以被取消。

          【讨论】:

            【解决方案9】:

            使用这个库

            https://github.com/alessio-santacroce/multiline-string-literals

            可以这样写

            System.out.println(newString(/*
                  Wow, we finally have
                  multiline strings in
                  Java! HOOO!
            */));
            

            非常好用且简单,但仅适用于单元测试

            【讨论】:

              【解决方案10】:

              这个问题的两个答案:

              1. 如果您想坚持使用纯 Java,Java 14 将于 2020 年 3 月发布,您可以利用 JEP 368 - 文本块,处于第二预览模式。实际上,该功能在其他版本中处于预览模式(至少有 13 个)。我创建并设置了示例集here。
              2. 虽然此功能很有用,但它很容易被滥用。请记住,Java 需要编译——在你的代码中包含大型字符数组可能是一个让你自责的简单方法(如果你想快速改变,你将需要重新编译——那个工具集可能对操作你的应用程序的人不可用) .

              根据我的经验,建议在配置文件中保留大字符串(通常是应用程序操作员可以/应该在运行时更改的字符串)。

              总结:负责任地使用文本块:)。

              【讨论】:

                【解决方案11】:

                一个简单的选择是使用像 SciTE (http://www.scintilla.org/SciTEDownload.html) 这样的编辑器来编辑您的 java 代码,它允许您包装文本,以便轻松查看和编辑长字符串。如果您需要转义字符,只需将它们放入。通过关闭 wrap-option,您可以检查您的字符串确实仍然只是一个长的单行字符串。当然,如果不是,编译器也会告诉你。

                我不知道 Eclipse 或 NetBeans 是否支持在编辑器中进行文本换行,因为它们有很多选择。但如果没有,那将是一件好事。

                【讨论】:

                • 或者,您可以使用 SciTE 以换行模式编辑您的字符串,将其双引号,然后将其复制并粘贴到 Eclipse。
                • 正如其他人建议的那样,最好将长字符串保存在外部文件中,这样您就可以修改它们而无需重新编译,您可以为不同的受众选择不同的资源目录等。缺点是这需要一些开销,知道从哪里读取文件等。我希望有一个简单但完整的代码示例来说明如何最好地做到这一点
                【解决方案12】:

                读到确实为 jdk7 计划了多行语法,我有点恼火(在 Java 存在大约几十年之后?)。有趣的是,甚至还没有用于读取文件完整内容的 readAll() 函数(仅来自 jdk7,呵呵),所以下面的代码读取单行。

                /* MakeMultiline v1.0 (2010) - 免费使用和复制。 将文本 blob 转换为一个 java 字符串文字的小工具 (在行中进行拆分,在每一端添加 \n 并封闭 双引号)。是否对文本 blob 中遇到的引号进行转义。 对于解决 java 中缺少的多行字符串语法很有用 之前的jdk7。用于: java MakeMultiline " " 要么 java MakeMultiline " " mytextfile.txt */ 导入java.io.*; 类 MakeMultiline { 公共静态无效主要(字符串[]参数){ 尝试 { // args[0]: 缩进 // args[1]: 要读取的文件名(可选;如果没有给出标准输入) // 使用标准输入时要注意末尾换行的 nmb! 字符串缩进 = (args.length > 0 ? args[0] : ""); 文件阅读器 fr = null; BufferedReader br; 如果 (args.length > 1) { fr = new FileReader(args[1]); br = new BufferedReader(fr); } 别的 { br = new BufferedReader(new InputStreamReader(System.in)); } 字符串 s;字符串 res = ""; while((s = br.readLine()) != null) { if (res.length() > 0) res += " +\n"; res += 缩进 + "\"" + s.replace("\"", "\\\"") + "\\n\""; } br.close(); if (fr != null) fr.close(); System.out.println(res + ";"); } 捕获(异常 e){ System.out.println("异常:" + e); } } }

                这对我来说是最快的解决方案。 (2010-01-27)

                【讨论】:

                  【解决方案13】:

                  如果作者有兴趣处理需要具有一些动态值的某种格式化的大字符串,这个问题并不完全清楚,但如果是这种情况,像 StringTemplate (http://www.stringtemplate.org/) 这样的模板引擎可能会非常有用.

                  下面是一个使用 StringTemplate 的简单代码示例。实际的模板(“Hello,”)可以从外部纯文本文件中加载。模板中的所有缩进都会被保留,不需要转义。

                  import org.stringtemplate.v4.*;
                   
                  public class Hello {
                      public static void main(String[] args) {
                          ST hello = new ST("Hello, <name>");
                          hello.add("name", "World");
                          System.out.println(hello.render());
                      }
                  }
                  

                  附:出于可读性和本地化目的,从源代码中删除大量文本始终是一个好主意。

                  【讨论】:

                    【解决方案14】:

                    您可以使用 Kotlin 多行字符串支持..

                    只需创建一个 Kotlin 类,并在其中使用它,

                    您可以将 kotlin 类称为 java synxtax

                    //科特林

                        fun jsIncreaseOrDecreaseWebFonts(webView : YHYWebView, sliderValue : Int, intFontSize : Int){
                                var initialData = """
                                      bla bla
                                    """
                    
                    }
                    

                    //来自Java

                     KotlinFunctions t = new KotlinFunctions();
                     t.jsIncreaseOrDecreaseWebFonts(webView,progresValue,incOrDec);
                    

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2019-08-02
                      • 2011-08-15
                      • 1970-01-01
                      相关资源
                      最近更新 更多