如何计算字符串中的格式字符数?

时间:2015-01-25 04:06:05

标签: java android string string-formatting

[更新]

我有一个可以包含格式字符的java字符串(带有%的字符串)。

当我们调用String.format(String, args..)

时,有没有办法计算此字符串所需的参数数量?

例如:

String placeholderString = "the %s jumped over the %s, %d times";
String.format(placeholderString, "cow", "moon", 2);

需要至少3个参数,否则会抛出MissingFormatArgumentException

String placeholderString = "the %1$s jumped over the %1$s, %2$d%% %n";
String.format(placeholderString, "cow", 2);

需要至少2个参数。

使用格式字符计算字符串所需的最小参数数量的优雅高效方法是什么?

我需要这个的原因是因为我希望能够在运行时为方法提供参数。如果参数超过最小计数,我想采用那些“未使用”的参数并将它们附加到字符串的末尾。您还可以建议我采用其他方法来满足此要求。

例如:我可以递增地提供参数,直到没有抛出MissingFormatArgumentException。然后我可以采取其余的论点并附加它们。这解决了我的问题,但我想知道这是否是最好的方法。

2 个答案:

答案 0 :(得分:1)

这也接受不正确的格式,但格式解析器显然太昂贵(即工作量太大) - 尽管这可能比正则表达式匹配更有效。这是否优雅我无法说 - 可能的输入变化太复杂,不适合光滑或光滑的方法。

我已经改变了我的第一个解决方案,它只会返回最小数量的参数,因为添加错误类型的参数可能会导致异常:您应该使用与转换代码匹配的类的参数。使用null会导致警告和丑陋的输出。

Pattern pat = Pattern.compile( "(?!<%)%" +
                  "(?:(\\d+)\\$)?" +
                  "([-#+ 0,(]|<)?" +
                  "\\d*" +
                  "(?:\\.\\d+)?" +
                  "(?:[bBhHsScCdoxXeEfgGaAtT]|" +
                  "[tT][HIklMSLNpzZsQBbhAaCYyjmdeRTrDFc])" );

Class<?> classOf( String conv ){
    if( conv.startsWith( "t" ) ){
        return Date.class;
    }
    switch( conv.charAt( 0 ) ){
    case 'b':
        return Boolean.class;
    case 'h': case 'd': case 'o': case 'x':
        return Integer.class;
    case 's':
        return String.class;
    case 'c':
        return Character.class;
    case 'e': case 'f': case 'g': case 'a':
        return Double.class;
    default:
        return Void.class;
    }
}

List<Class<?>> count( String fmt ){
    List<Class<?>> res = new ArrayList<>();
    Matcher m = pat.matcher( fmt );
    while( m.find() ){
        if( m.group(1) != null ){
            String dec = m.group(1);
            int ref = Integer.parseInt( dec );
            if( res.size() < ref ){
                while( res.size() < ref - 1 ){
                    res.add( Void.class );
                }
                res.add( classOf( m.group(3).toLowerCase() )) ;
            } else {
                Class<?> clazz = classOf( m.group(3).toLowerCase() );
                res.set( ref - 1, clazz );
            }
        } else if( m.group(2) != null && "<".equals( m.group(2) ) ){
            // ignore
        } else {
            res.add( classOf( m.group(3).toLowerCase() ));
        }
    }
    return res;
}

使用

进行测试
void demo( String... formats ){
    for( String fmt: formats ){
        List<Class<?>> res = count( fmt );
        System.out.print( fmt + ": " + res.size() + ":" );
        for( Class<?> clazz: res ){
            System.out.print( " " + clazz.getSimpleName() );
        }
        System.out.println();
    }
}

输出:

%d: 1: Integer
%1$d: 1: Integer
the %s jumped over the %s, %d times: 3: String String Integer
the %1$s jumped over the %1$s, %2$d%% %n: 2: String Integer
Duke's Birthday: %1$tm %1$te,%1$tY: 1: Date
Duke's Birthday: %1$tm %<te,%<tY: 1: Date
%4$s %3$s %2$s %1$s %4$s %3$s %2$s %1$s: 4: String String String String
%s %s %<s %<s: 2: String String
%s %s %s %s: 4: String String String String
%2$s %s %<s %s: 4: Void String String String

这是简单的版本,只返回计数:

int count( String fmt ){
    Matcher m = pat.matcher( fmt );
    int np = 0;
    int maxref = 0;
    while( m.find() ){
        if( m.group(1) != null ){
            String dec = m.group(1);
            int ref = Integer.parseInt( dec );
            maxref = Math.max( ref, maxref );
        } else if( m.group(2) != null && "<".equals( m.group(2) ) ){
            // ignore
        } else {
            np++;
        }
    }
    return Math.max( np, maxref );
}

答案 1 :(得分:0)

如果只想计数,则可以使用正则表达式(?<!%)%(?!%)