Search code examples
javastring-formattingformat-specifiers

way to overcome MissingFormatArgumentException for String.format when there aren't enough arguments?


On Java, I know that the next thing is ok:

String test="aaa";
System.out.println(String.format(test,"asd"));

(prints "aaa")

However, I want to be able to handle the opposite thing, such as:

String test="aaa%sbbb";
System.out.println(String.format(test));

(this produces an exception java.util.MissingFormatArgumentException )

I wish to make it as general as possible, no matter how many specifiers/parameters are there, if there aren't enough values, just ignore them (skip all of the specifiers from the problematic position) and write the rest of the string (in the case I've shown, for example, it will write "aaabbb" ) .

Is it possible out of the box, or should I write a function that does it?


Solution

  • ok, i think i've got a working solution, and i also think it supports everything that String.format supports:

    public static String formatString(final String stringToFormat,final Object... args)
      {
      if(stringToFormat==null||stringToFormat.length()==0)
        return stringToFormat;
      int specifiersCount=0;
      final int argsCount=args==null ? 0 : args.length;
      final StringBuilder sb=new StringBuilder(stringToFormat.length());
      for(int i=0;i<stringToFormat.length();++i)
        {
        char c=stringToFormat.charAt(i);
        if(c!='%')
          sb.append(c);
        else
          {
          final char nextChar=stringToFormat.charAt(i+1);
          if(nextChar=='%'||nextChar=='n')
            {
            ++i;
            sb.append(c);
            sb.append(nextChar);
            continue;
            }
          // found a specifier
          ++specifiersCount;
          if(specifiersCount<=argsCount)
            sb.append(c);
          else while(true)
            {
            ++i;
            c=stringToFormat.charAt(i);
            // find the end of the converter, to ignore it all
            if(c=='t'||c=='T')
              {
              // time prefix and then a character, so skip it
              ++i;
              break;
              }
            if(c>='a'&&c<='z'||c>='A'&&c<='Z')
              break;
            }
          }
        }
      return String.format(sb.toString(),args);
      }
    

    and a test, just to show it works:

    System.out.println(formatString("aaa%sbbb"));
    System.out.println(formatString("%da%sb%fc%tBd%-15se%16sf%10.2fg"));
    

    sadly, it creates a new stringBuilder and a string on the way, but it works.