我有以下类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
答案 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"));