在java正则表达式中处理美元字符的最佳方法

时间:2012-10-23 13:12:16

标签: java regex

我有以下类PlaceHolderConverter用于将类似"my {} are beautiful"的字符串解析为带有填充变量的字符串。

例如new PlaceHolderConverter("\\{\\}").format("my {} are beautiful", "flowers")将返回字符串"my flowers are beautiful"

package something;


import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class PlaceHolderConverter
{
    public Pattern lookForVar;

    public PlaceHolderConverter( String placeHolder )
    {
        this.lookForVar = Pattern.compile( placeHolder );
    }

    public String format( String text, String... args )
    {
        if ( args == null || args.length == 0 )
        {
            return text;
        }
        StringBuffer stringBuffer = new StringBuffer();
        Matcher matcher = lookForVar.matcher( text );
        short varCount = 0;
        while ( matcher.find() )
        {
            matcher.appendReplacement( stringBuffer, args[varCount++] );
        }
        matcher.appendTail( stringBuffer );
        return stringBuffer.toString();
    }
}

正如您在以下测试中所看到的,我遇到了特殊字符美元的问题,因为它是java正则表达式的特殊字符。 我试图用Pattern.quote()解决这个问题,但没有结果。

package something;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

import java.util.regex.Pattern;

public class PlaceHolderConverterTest
{
    private PlaceHolderConverter placeHolderConverter;

    @Before
    public void before()
    {
        placeHolderConverter = new PlaceHolderConverter( "\\{\\}" );
    }

    @Test // SUCCESS
    public void whenStringArgsThenReplace()
    {
        String result = placeHolderConverter.format( "My {} are beautifull", "flowers" );
        Assert.assertEquals( "My flowers are beautifull", result );
    }

    @Test // FAIL IllegalArgumentException illegal group reference while calling appendReplacement
    public void assertEscapeDollar()
    {
        String result = placeHolderConverter.format( "My {} are beautiful", "flow$ers" );
        Assert.assertEquals( "My flow$ers are beautiful", result );
    }

    @Test // FAIL IllegalArgumentException illegal group reference while calling appendReplacement
    public void assertEscapeDollarWithQuote()
    {
        String result = placeHolderConverter.format( "My {} are beautiful", Pattern.quote("flow$ers") );
        Assert.assertEquals( "My flow$ers are beautiful", result );
    }

}

我还试图在regexp中使用它之前手动逃避美元,例如.replaceAll("\\$", "\\\\$"),但似乎replaceAll不喜欢arg1包含在arg2中。

我如何解决这个问题?

此处可以提供补丁https://gist.github.com/3937872

4 个答案:

答案 0 :(得分:4)

当替换固定字符串时,不需要在String上调用regex方法,因为有一个更简单的方法:input.replace("$", "\\$");。使用这种方法,您不会遇到由美元符号的特殊含义引起的任何麻烦,并且它会(非常轻微)更快地作为奖励。

答案 1 :(得分:3)

解释Pattern.quote()失败的原因:

Pattern.quote()旨在用于正则表达式(意为搜索表达式)。它的工作原理是用"\\Q""\\E"包围字符串,分别表示“逐字开头部分”和“逐字结尾部分”。

您的错误来自替换字符串中未转义的$,该字符串不是正则表达式,因此无法使用Pattern.quote()正确转义。因此,正确的解决方案是在替换字符串中手动转义美元符号:

String resultString = subjectString.replaceAll("\\$", "\\\\\\$");

或(因为您不需要使用正则表达式进行单个字符替换),使用

String resultString = subjectString.replace("$", "\\$");

答案 2 :(得分:2)

这是一个简单的解决方案,它涵盖了您的测试用例:

public static String replace( String str, String placeholderRegex, Object... args ) {
    String repl = str.replaceAll( placeholderRegex, "%s" );
    return String.format( repl, args );
}

让我们检查一下:

public static void main( String[] args ) {
    System.out.println( 
            replace( "my {} are beautifull {} test", 
                     "\\{\\}", 
                     "flowers", "$dollar" ) );
}

但是,当然,如果你必须处理%个字符,你必须稍微修改一下replace函数(在替换之前转义%,然后转换为unescape)。您也可以使用预编译的正则表达式(如在您的解决方案中)。

答案 3 :(得分:0)

对于记录,您可以让Java在您的任何替换序列中为您(或其他特殊字符,如'\')转义'$'符号。这样,如果愿意,您仍然可以使用String.replaceAll()。

String s = "input".replaceAll("pattern", Matcher.quoteReplacement("replacement"));

(See Java Doc)