【问题标题】:How to format strings in Java如何在 Java 中格式化字符串
【发布时间】:2011-09-19 21:21:06
【问题描述】:

原始问题,但我如何格式化这样的字符串:

“第 {2} 步的第 {1} 步”

通过使用 Java 替换变量?在 C# 中这很容易。

【问题讨论】:

标签: java string string-formatting


【解决方案1】:

字符串#格式

格式化字符串最常用的方法是使用这种静态方法,该方法自 Java 5 起就长期可用,并且有两个重载方法:

该方法易于使用,format 模式由底层formatter 定义。

String step1 = "one";
String step2 = "two";

// results in "Step one of two"
String string = String.format("Step %s of %s", step1, step2); 

您可以传递Locale 以尊重语言和区域规范。有关详细信息,请参阅此答案:https://stackoverflow.com/a/6431949/3764965(感谢 Martin Törnwall)。

消息格式

MessageFormat 类从 Java 的第一个版本开始可用,适用于国际化。在最简单的形式中,有一个用于格式化的静态方法:

String step1 = "one";
String step2 = "two";

// results in "Step one of two"
String string = MessageFormat.format("Step {0} of {1}", step1, step2);

请记住 MessageFormat 遵循不同于 String#format 的特定模式,请参阅其 JavaDoc 了解更多详细信息:MessageFormat - patterns

可以使用Locale,但是,必须实例化类的对象并将其传递给构造函数,因为上面的静态方法使用具有默认语言环境的默认构造函数。有关更多信息,请参阅此答案:https://stackoverflow.com/a/6432100/3764965(归功于ataylor)。

非标准 JDK 解决方案

有很多方法可以使用外部库来格式化字符串。如果仅出于字符串格式化的目的导入库,它们几乎没有任何好处。几个例子:

  • Apache Commons:StringSubstitutor,其 JavaDoc 中的示例。
  • Cactoos:FormattedText,示例here
  • 有趣的是,Guava 不打算添加格式或模板功能:#1142
  • ...和其他自定义实现。

请随意添加更多内容,但我找不到进一步扩展此部分的理由。

Java 15 之后的替代方案

从 Java 15 开始,有一个名为 String#formatted(Object... args) 的新实例方法。

内部实现同String#format(String format, Object... args)

使用此字符串作为格式字符串和提供的参数的格式。

String step1 = "one";
String step2 = "two";

// results in "Step one of two"
String string = "Step %s of %s".formatted(step1, step2);     

优点:区别在于方法不是static,而格式化模式是一个字符串本身,根据@987654355创建一个新的@。这允许链接首先构建格式本身。

缺点Locale没有重载方法,所以使用默认的。如果您需要使用自定义Locale,则必须坚持使用String#format(Locale l, String format, Object... args)

【讨论】:

  • 不错!太糟糕了,我现在仍在使用 Java 8。我们正在讨论在明年左右迁移到 Java 11 还是 Java 14...所以我们可能在接下来的 3-5 年内都无法使用此功能。
  • 我会更乐观。自 2021 年 9 月起,LTS 版本将是 Java 17,因此此功能可能会派上用场。我没有注意到像 8 和 9 版本之间的模块化系统这样的结构变化,所以我相信迁移会更快:))
【解决方案2】:

Apache Commons StringSubstitutor 提供了一种简单易读的方式来使用命名变量格式化 Strings。

import org.apache.commons.text.StringSubstitutor;
// ...
Map<String, String> values = new HashMap<>();
values.put("animal", "quick brown fox");
values.put("target", "lazy dog");
StringSubstitutor sub = new StringSubstitutor(values);
String result = sub.replace("The ${animal} jumped over the ${target}.");
// "The quick brown fox jumped over the lazy dog."

该类支持为变量提供默认值。

String result = sub.replace("The number is ${undefined.property:-42}.");
// "The number is 42."

要使用递归变量替换,请调用setEnableSubstitutionInVariables(true);

Map<String, String> values = new HashMap<>();
values.put("b", "c");
values.put("ac", "Test");
StringSubstitutor sub = new StringSubstitutor(values);
sub.setEnableSubstitutionInVariables(true);
String result = sub.replace("${a${b}}");
// "Test"

【讨论】:

    【解决方案3】:

    来自 Apache Commons Text 的 org.apache.commons.text.StringSubstitutor 帮助器类提供命名变量替换

    Map<String, String> valuesMap = new HashMap<>();
    valuesMap.put("animal", "quick brown fox");
    valuesMap.put("target", "lazy dog");
    String resolved = new StringSubstitutor(valuesMap).replace("The ${animal} jumped over the ${target}.");
    System.out.println(resolved); // The quick brown fox jumped over the lazy dog.
    

    【讨论】:

      【解决方案4】:

      我写了这个函数,它做了正确的事情。用同名变量的值插入以$ 开头的单词。

      private static String interpol1(String x){
          Field[] ffield =  Main.class.getDeclaredFields();
          String[] test = x.split(" ") ;
          for (String v : test ) {
              for ( Field n: ffield ) {
                  if(v.startsWith("$") && ( n.getName().equals(v.substring(1))  )){
                      try {
                          x = x.replace("$" + v.substring(1), String.valueOf( n.get(null)));
                      }catch (Exception e){
                          System.out.println("");
                      }
                  }
              }
          }
          return x;
      }
      

      【讨论】:

        【解决方案5】:

        看看String.format。但是请注意,它采用类似于 C 的 printf 系列函数的格式说明符——例如:

        String.format("Hello %s, %d", "world", 42);
        

        将返回“Hello world,42”。在了解格式说明符时,您可能会发现 this 很有帮助。 Andy Thomas-Cramer 很友好地在下面的评论中留下了this 链接,这似乎指向了官方规范。最常用的是:

        • %s - 插入一个字符串
        • %d - 插入一个有符号整数(十进制)
        • %f - 插入一个实数,标准符号

        这与 C# 完全不同,C# 使用带有可选格式说明符的位置引用。 这意味着你不能做这样的事情:

        String.format("The {0} is repeated again: {0}", "word");
        

        ... 实际上没有重复传递给 printf/format 的参数。(请参阅下面的 Scrum Meister 评论)


        如果您只想直接打印结果,您可以根据自己的喜好找到 System.out.printf (PrintStream.printf)。

        【讨论】:

        • 另一个选项是 java.text.MessageFormat,它接受 {1} 样式格式符号。 String.format() 的格式符号可以类似于 C 的 printf() 格式符号——但也可以不同。有关完整语法,请参阅 download.oracle.com/javase/6/docs/api/java/util/…
        • String.format 也可以取数字位置:String.format("%2$s %1$s", "foo", "bar"); 将输出bar foo
        • 从 Java 15 开始,可以使用 String.formatted() 直接在字符串上使用 String.format。 "Hello %s, %d".formatted("world", 42)
        【解决方案6】:

        这个解决方案对我有用。我需要为 REST 客户端动态创建 url,所以我创建了这个方法,所以你只需要像这样传递 restURL

        /customer/{0}/user/{1}/order
        

        并根据需要添加尽可能多的参数:

        public String createURL (String restURL, Object ... params) {       
            return new MessageFormat(restURL).format(params);
        }
        

        你只需要像这样调用这个方法:

        createURL("/customer/{0}/user/{1}/order", 123, 321);
        

        输出

        "/customer/123/user/321/order"

        【讨论】:

          【解决方案7】:
          public class StringFormat {
          
              public static void main(String[] args) {
                      Scanner sc=new Scanner(System.in);
                      System.out.println("================================");
                      for(int i=0;i<3;i++){
                          String s1=sc.next();
                          int x=sc.nextInt();
                          System.out.println(String.format("%-15s%03d",s1,x));
                      }
                      System.out.println("================================");
          
              }
          }
          

          前哨 "================================="
          ved15space123 ved15space123 ved15space123 "=================================

          Java 解决方案

          • “-”用于左缩进

          • “15”使字符串占用的最小长度为 15

          • “s”(% 后面的几个字符)将被我们的字符串替换
          • 0 用左边的 0 填充我们的整数
          • 3 使我们的整数最小长度为 3
          【解决方案8】:

          我已经为它写了我的简单方法:

          public class SomeCommons {
              /** Message Format like 'Some String {0} / {1}' with arguments */
              public static String msgFormat(String s, Object... args) {
                  return new MessageFormat(s).format(args);
              }
          }
          

          所以你可以把它用作:

          SomeCommons.msfgFormat("Step {1} of {2}", 1 , "two");
          

          【讨论】:

            【解决方案9】:

            如果选择不使用 String.format,另一个选项是 + 二元运算符

            String str = "Step " + a + " of " + b;
            

            这相当于

            new StringBuilder("Step ").append(String.valueOf(1)).append(" of ").append(String.valueOf(2));

            使用哪个是您的选择。 StringBuilder 更快,但速度差异很小。我更喜欢使用 + 运算符(它执行 StringBuilder.append(String.valueOf(X))) 并发现它更易于阅读。

            【讨论】:

            • 如果你要否定我,请解释原因。
            • 1) 在回答有关字符串格式的问题时,您解释了+ 运算符的工作原理。 2)你的解释甚至不准确。 + 相当于使用StringBuilder,而不是String.concat(Way too much info on this.)
            【解决方案10】:

            除了String.format,还可以看看java.text.MessageFormat。格式不太简洁,更接近您提供的 C# 示例,您也可以使用它进行解析。

            例如:

            int someNumber = 42;
            String someString = "foobar";
            Object[] args = {new Long(someNumber), someString};
            MessageFormat fmt = new MessageFormat("String is \"{1}\", number is {0}.");
            System.out.println(fmt.format(args));
            

            一个更好的示例利用 Java 1.5 中的可变参数和自动装箱改进,并将上述内容变成单行:

            MessageFormat.format("String is \"{1}\", number is {0}.", 42, "foobar");
            

            MessageFormat 更适合使用选择修饰符进行 i18nized 复数。要指定在变量为 1 时正确使用单数形式而在其他情况下使用复数形式的消息,您可以执行以下操作:

            String formatString = "there were {0} {0,choice,0#objects|1#object|1<objects}";
            MessageFormat fmt = new MessageFormat(formatString);
            fmt.format(new Object[] { new Long(numberOfObjects) });
            

            【讨论】:

            • MessageFormat 用于本地化目的,因此在使用时要小心。例如,以下代码 MessageFormat.format("Number {0}", 1234)); 取决于默认语言环境可以生成 Number 1,234 而不是 Number 1234
            • @ataylor :你好,对不起,我有点困惑。我想要类似的东西,我将传递具有数据的类对象,当{0} it ll take firstname 时,当{1} then lastname 时,就像这样。有没有可能像{0,choice,0.getFirstName()} 或类似的东西?
            • @user3145373ツ 我不这么认为。
            • 好的,我已经推荐了一个我想要的 .Net 项目,codeproject.com/Articles/42310/…,请参考这个项目,我看起来像这样。如果您知道任何可用的项目或软件包,请告诉我。谢谢
            猜你喜欢
            • 2010-12-26
            • 2015-06-22
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2017-10-27
            • 2012-08-16
            • 1970-01-01
            相关资源
            最近更新 更多