【问题标题】:Get number of placeholders in Formatter.format() String获取 Formatter.format() String 中占位符的数量
【发布时间】:2016-05-24 12:37:29
【问题描述】:

在 Java 程序中,我使用带有 Formatter.format() 函数的字符串,该函数是从服务器获取的。我不能确定要格式化的String 有占位符或有效数量。如果 String 与预期不符,我想抛出异常 - 或以某种方式记录它。

此时我不在乎占位符的类型是什么(StringInteger、...),我只想获取每个字符串的预期参数数量。

实现这一目标的最简单方法是什么?一种方法是使用正则表达式,但我在想是否有更方便的方法——例如内置函数。

这里有一些例子:

Example String | number of placeholders:
%d of %d          | 2
This is my %s     | 1
A simple content. | 0
This is 100%      | 0
Hi! My name is %s and I have %d dogs and a %d cats. | 3

编辑: 如果提供的参数不足,Formatter.format() 会抛出异常。我有可能得到一个没有占位符的字符串。在这种情况下,即使我提供了参数(将被省略),也不会抛出异常(即使我想抛出一个),只会返回字符串值。我需要向服务器报告错误。

【问题讨论】:

  • 在什么情况下“不是你所期望的”?大概您知道您期望的占位符的数量 - 您可以尝试使用默认值进行格式化,看看它是否失败?
  • 我不知道内置函数,但我的第一个想法是正则表达式是要走的路。您对使用正则表达式有什么特别的顾虑吗?
  • @AndyTurner 我不够清楚。我知道如果我没有提供足够的参数,格式方法会引发异常。我的问题是,即使我提供了太多参数,我也想抛出异常,这就是为什么我需要占位符的数量。
  • 所以,尝试使用非同一个默认值进行格式化。如果失败,则有正确数量的占位符;如果成功,则占位符太少。
  • @Hill 实际上并不是这样,而是在各种情况下测试了内置函数,因此编写我自己的正则表达式可能会产生额外的错误。我认为这是它应该具备的功能

标签: java string string-formatting formatter string.format


【解决方案1】:

您可以使用定义占位符格式的正则表达式来计算字符串中的匹配总数。

// %[argument_index$][flags][width][.precision][t]conversion
String formatSpecifier
    = "%(\\d+\\$)?([-#+ 0,(\\<]*)?(\\d+)?(\\.\\d+)?([tT])?([a-zA-Z%])";
// The pattern that defines a placeholder
Pattern pattern = Pattern.compile(formatSpecifier);
// The String to test
String[] values = {
    "%d of %d",
    "This is my %s", 
    "A simple content.", 
    "This is 100%", "Hi! My name is %s and I have %d dogs and a %d cats."
};
// Iterate over the Strings to test
for (String value : values) {
    // Build the matcher for a given String
    Matcher matcher = pattern.matcher(value);
    // Count the total amount of matches in the String
    int counter = 0;
    while (matcher.find()) {
        counter++;
    }
    // Print the result
    System.out.printf("%s=%d%n", value, counter);
}

输出:

%d of %d=2
This is my %s=1
A simple content.=0
This is 100%=0
Hi! My name is %s and I have %d dogs and a %d cats.=3

【讨论】:

    【解决方案2】:

    我已经修改了String.format()的代码。结果如下:

    private static final String formatSpecifier
        = "%(\\d+\\$)?([-#+ 0,(\\<]*)?(\\d+)?(\\.\\d+)?([tT])?([a-zA-Z%])";
    
    private static Pattern fsPattern = Pattern.compile(formatSpecifier);
    
    private static int parse(String s) {
      int count = 0;
      Matcher m = fsPattern.matcher(s);
      for (int i = 0, len = s.length(); i < len; ) {
        if (m.find(i)) {
          // Anything between the start of the string and the beginning
          // of the format specifier is either fixed text or contains
          // an invalid format string.
          if (m.start() != i) {
            // Make sure we didn't miss any invalid format specifiers
            checkText(s, i, m.start());
            // Assume previous characters were fixed text
          }
    
          count++;
          i = m.end();
        } else {
          // No more valid format specifiers.  Check for possible invalid
          // format specifiers.
          checkText(s, i, len);
          // The rest of the string is fixed text
          break;
        }
      }
      return count;
    }
    
    private static void checkText(String s, int start, int end) {
      for (int i = start; i < end; i++) {
        // Any '%' found in the region starts an invalid format specifier.
        if (s.charAt(i) == '%') {
          char c = (i == end - 1) ? '%' : s.charAt(i + 1);
          throw new UnknownFormatConversionException(String.valueOf(c));
        }
      }
    }
    

    测试:

    public static void main(String[] args) {
      System.out.println(parse("Hello %s, My name is %s. I am %d years old."));
    }
    

    输出:

    3

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-07-23
      • 2017-07-27
      • 2018-01-25
      • 2013-09-13
      • 2023-03-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多