【问题标题】:Sprintf equivalent in JavaJava 中的 Sprintf 等价物
【发布时间】:2010-09-08 00:05:37
【问题描述】:

Printf 在 1.5 版本中添加到 Java,但我似乎无法找到如何将输出发送到字符串而不是文件(这是 sprintf 在 C 中所做的)。有谁知道如何做到这一点?

【问题讨论】:

    标签: java string formatting


    【解决方案1】:
    // Store the formatted string in 'result'
    String result = String.format("%4d", i * j);
    
    // Write the result to standard output
    System.out.println( result );
    

    参见format 及其syntax

    【讨论】:

      【解决方案2】:

      字符串是不可变的类型。你不能修改它们,只能返回新的字符串实例。

      因此,使用实例方法进行格式化没有什么意义,因为它必须像这样调用:

      String formatted = "%s: %s".format(key, value);
      

      最初的 Java 作者(和 .NET 作者)认为静态方法在这种情况下更有意义,因为您不会修改目标,而是调用格式方法并传入输入字符串。

      这是一个示例,说明为什么 format() 作为实例方法会很笨。在 .NET 中(可能在 Java 中),Replace() 是一个实例方法。

      你可以这样做:

       "I Like Wine".Replace("Wine","Beer");
      

      但是,什么都没有发生,因为字符串是不可变的。 Replace() 尝试返回一个新字符串,但它没有被赋值。

      这会导致很多常见的新手错误,例如:

      inputText.Replace(" ", "%20");
      

      同样,什么也没有发生,而是你必须做:

      inputText = inputText.Replace(" ","%20");
      

      现在,如果您了解字符串是不可变的,那就完全有道理了。如果你不这样做,那么你只是感到困惑。 Replace() 的正确位置是format() 所在的位置,作为String 的静态方法:

       inputText = String.Replace(inputText, " ", "%20");
      

      现在毫无疑问会发生什么。

      真正的问题是,为什么这些框架的作者决定一个应该是实例方法,而另一个应该是静态的?在我看来,两者都更优雅地表示为静态方法。

      不管你怎么看,事实是你使用静态版本更不容易出错,而且代码更容易理解(No Hidden Gotchas)。

      当然有一些方法可以完美的作为实例方法,取String.Length()

      int length = "123".Length();
      

      在这种情况下,很明显我们并没有尝试修改“123”,我们只是检查它并返回它的长度。这是实例方法的完美候选。

      我对不可变对象的实例方法的简单规则:

      • 如果您需要返回相同类型的新实例,请使用静态方法。
      • 否则,使用实例方法。

      【讨论】:

      • 我看到你不知何故明白了我建议修改格式字符串的想法。我从来没有考虑过有人可能期望 String 改变的可能性,因为它们的不变性是如此基本。
      • 看到格式字符串通常更像“价格是 %4d”,而不是“%4d”,我仍然看到很多混淆的可能性。你对静态方法有什么看法? :)
      • 这个答案似乎与问题无关。
      • 答案甚至不是 Java,似乎与 .NET 更相关
      • -1。否决b / c它是切线的。不一定是不可变对象的最佳风格。这种风格比简单的方法调用更冗长,特别是对于链式操作。它放弃了运行时多态性,因为它调用了静态方法,这很重要。 YMMV。
      【解决方案3】:

      从 Java 13 开始,您在 String 上拥有 formatted 1 方法,该方法与文本块一起添加为预览功能 2。 你可以用它来代替String.format()

      Assertions.assertEquals(
         "%s %d %.3f".formatted("foo", 123, 7.89),
         "foo 123 7.890"
      );
      

      【讨论】:

        【解决方案4】:

        两种解决方案都可以模拟 printf,但方式不同。 例如,要将值转换为十六进制字符串,您有以下两种解决方案:

        • format(),最接近sprintf()

          final static String HexChars = "0123456789abcdef";
          
          public static String getHexQuad(long v) {
              String ret;
              if(v > 0xffff) ret = getHexQuad(v >> 16); else ret = "";
              ret += String.format("%c%c%c%c",
                  HexChars.charAt((int) ((v >> 12) & 0x0f)),
                  HexChars.charAt((int) ((v >>  8) & 0x0f)),
                  HexChars.charAt((int) ((v >>  4) & 0x0f)),
                  HexChars.charAt((int) ( v        & 0x0f)));
              return ret;
          }
          
        • 使用replace(char oldchar , char newchar),速度稍快但相当有限:

              ...
              ret += "ABCD".
                  replace('A', HexChars.charAt((int) ((v >> 12) & 0x0f))).
                  replace('B', HexChars.charAt((int) ((v >>  8) & 0x0f))).
                  replace('C', HexChars.charAt((int) ((v >>  4) & 0x0f))).
                  replace('D', HexChars.charAt((int) ( v        & 0x0f)));
              ...
          
        • 还有第三种解决方案,只需将 char 一个一个地添加到 ret(char 是 彼此相加的数字!),例如:

          ...
          ret += HexChars.charAt((int) ((v >> 12) & 0x0f)));
          ret += HexChars.charAt((int) ((v >>  8) & 0x0f)));
          ...
          

        ...但那会是真的丑陋的。

        【讨论】:

        • 所有好主意,但会将您的代码变成您的同事无法理解的只写代码。
        【解决方案5】:

        您可以使用 PrintStream 对任何输出流执行 printf。 不知何故,打印成字符串流:

        PrintStream ps = new PrintStream(baos);
        ps.printf("there is a %s from %d %s", "hello", 3, "friends");
        System.out.println(baos.toString());
        

        这会输出以下文本有 3 个朋友打招呼 可以像这样创建字符串流 ByteArrayOutputStream:

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        

        你可以积累多种格式:

        PrintStream ps = new PrintStream(baos);
        ps.printf("there is a %s from %d %s ", "hello", 3, "friends");
        ps.printf("there are %d % from a %", 2, "kisses", "girl");
        System.out.println(baos.toString());
        

        这输出有 3 个朋友打招呼,有 2 个女孩亲吻
        在 ByteArrayOutputStream 上调用 reset 以生成干净的新字符串

        ps.printf("there is a %s from %d %s", "flip", 5, "haters");
        baos.reset(); //need reset to write new string
        ps.printf("there are %d % from a %", 2, "kisses", "girl");
        System.out.println(baos.toString());
        

        输出将是一个女孩有 2 个吻

        【讨论】:

          猜你喜欢
          • 2010-12-04
          • 1970-01-01
          • 2019-11-03
          • 2016-07-16
          • 2011-10-06
          • 2012-05-09
          • 2023-03-04
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多