给定一个包含替换键的字符串,如何使用 Java 最有效地用运行时值替换这些键?我需要经常,快速地,以及相当长的字符串(例如,平均1-2kb)。键的形式是我的选择,因为我也在这里提供模板。
这是一个例子(请不要挂断它是XML;我想这样做,如果可能的话,比使用XSL或DOM操作便宜)。我想用bean属性,true @[^@]*?@
属性和一些其他来源中的属性值替换此中的所有Property
模式。这里的关键是快速。有什么想法吗?
<?xml version="1.0" encoding="utf-8"?>
<envelope version="2.3">
<delivery_instructions>
<delivery_channel>
<channel_type>@CHANNEL_TYPE@</channel_type>
</delivery_channel>
<delivery_envelope>
<chan_delivery_envelope>
<queue_name>@ADDRESS@</queue_name>
</chan_delivery_envelope>
</delivery_envelope>
</delivery_instructions>
<composition_instructions>
<mime_part content_type="application/xml">
<content><external_uri>@URI@</external_uri></content>
</mime_part>
</composition_instructions>
</envelope>
天真的实现是使用String.replaceAll()
,但我不禁认为这不太理想。如果我可以避免添加新的第三方依赖项,那就更好了。
答案 0 :(得分:6)
Matcher中的appendReplacement方法看起来很有用,虽然我不能保证它的速度。
以下是Javadoc的示例代码:
Pattern p = Pattern.compile("cat");
Matcher m = p.matcher("one cat two cats in the yard");
StringBuffer sb = new StringBuffer();
while (m.find()) {
m.appendReplacement(sb, "dog");
}
m.appendTail(sb);
System.out.println(sb.toString());
编辑:如果这很复杂,你可能很容易实现自己的状态机。你几乎正在做appendReplacement已经在做的事情,虽然专门的实现可能会更快。
答案 1 :(得分:4)
现在跳过编写自己的东西还为时过早。我会从天真的替换解决方案开始,并实际基准测试。然后我会尝试第三方模板解决方案。然后我会尝试自定义流版本。
在你获得一些难以理解的数字之前,你怎么能确定优化它的价值呢?
答案 2 :(得分:3)
Java是否有一种regexp replace()形式,其中函数被调用?
我被Javascript String.replace()方法破坏了。 (就此而言,您可以运行Rhino并使用Javascript,但不知何故我认为即使Javascript编译器/解释器有效,它也不会像纯Java调用一样快)
编辑:没关系,@ mmyers可能有最好的答案。
无偿的点 - 卑躬屈膝:(因为我想看看自己是否可以这样做:)Pattern p = Pattern.compile("@([^@]*?)@");
Matcher m = p.matcher(s);
StringBuffer sb = new StringBuffer();
while (m.find())
{
m.appendReplacement(sb,substitutionTable.lookupKey(m.group(1)));
}
m.appendTail(sb);
// replace "substitutionTable.lookupKey" with your routine
答案 3 :(得分:1)
你真的想写一些自定义的内容,这样你就可以避免多次处理字符串。我不能强调这一点 - 因为我看到的大多数其他解决方案看起来都忽略了这个问题。
可选择将文本转换为流。通过char读取char,将每个char转发到输出字符串/流,直到看到@然后读取到下一个@ slurping out键,将键替换为输出:重复直到流结束。
我知道这很粗野 - 但它可能是最好的。
我假设您对'@'有一些合理的假设,而不仅仅是'显示'输入中与您的令牌键无关。 :)
答案 4 :(得分:1)
请不要挂断它是XML;我希望这样做,如果可能的话,比使用XSL或DOM操作便宜
如果您还没有处理插入的字符串以进行字符转义,那么您的进程的下游将会挂起。这并不是说如果你有充分的理由你不能自己做,但是这意味着你要么必须确保你的模式都在文本节点中,你也正确地逃避替换文本。
@ Foo @对标准&amp; Foo有什么确切的优势;已经内置到Java附带的XML库中的语法?
答案 5 :(得分:1)
如果你不改变你的范式,文本处理总会受到限制。我不知道你的域名是多么灵活,所以不确定这是否适用,但这里有:
尝试创建一个索引到文本替换的位置 - 如果模板不经常更改,这是特别好的,因为它成为模板“编译”的一部分,成为可以接受所需值的二进制对象替换,并将整个字符串作为字节数组进行blit。此对象可以缓存/保存,下次重新替换新值以再次使用。也就是说,每次都可以节省解析文档的费用。 (实施留给读者的练习= D)
但是,在开始编写自定义模板引擎之前,请使用分析器检查这是否是您所说的瓶颈。问题实际上可能是其他地方。
答案 6 :(得分:1)
正如其他人所说,appendReplacement()和appendTail()是你需要的工具,但是你需要注意的事项。如果替换字符串包含任何美元符号,则该方法将尝试将它们解释为捕获组引用。如果有任何反斜杠(用于逃避美元唱歌),它会吃掉它们或抛出异常。
如果您的替换字符串是动态生成的,您可能不会事先知道它是否包含任何美元符号或反斜杠。为了防止出现问题,您可以将替换直接附加到StringBuffer,如下所示:
Pattern p = Pattern.compile("@([^@]*?)@");
Matcher m = p.matcher(s);
StringBuffer sb = new StringBuffer();
while (m.find())
{
m.appendReplacement("");
sb.append(substitutionTable.lookupKey(m.group(1)));
}
m.appendTail(sb);
您每次都需要调用appendReplacement(),因为这样可以让您与匹配位置保持同步。但是这个技巧避免了很多毫无意义的处理,这可能会给你带来明显的性能提升作为奖励。
答案 7 :(得分:1)
这是我使用的,来自apache commons项目 http://commons.apache.org/lang/api/org/apache/commons/lang/text/StrSubstitutor.html
答案 8 :(得分:0)
我还有一个基于非正则表达式的替换库,可用here。我没有测试它的速度,它不直接支持你的例子中的语法。但是扩展以支持该语法很容易;例如,请参阅this class。
答案 9 :(得分:0)
查看专门针对此的图书馆,例如Apache Velocity。如果没有别的,你可以打赌他们对这部分逻辑的实现很快。
答案 10 :(得分:0)
我不太确定接受的答案比String.replaceAll(String,String)更快。这里比较的是String.replaceAll的实现和在封面下使用的Matcher.replaceAll。看起来与OP正在寻找的非常相似,我猜它可能比这种简单的解决方案更加优化。
public String replaceAll(String s, String s1)
{
return Pattern.compile(s).matcher(this).replaceAll(s1);
}
public String replaceAll(String s)
{
reset();
boolean flag = find();
if(flag)
{
StringBuffer stringbuffer = new StringBuffer();
boolean flag1;
do
{
appendReplacement(stringbuffer, s);
flag1 = find();
} while(flag1);
appendTail(stringbuffer);
return stringbuffer.toString();
} else
{
return text.toString();
}
}
答案 11 :(得分:0)
...... Chii是对的。 如果这是一个必须运行很多次的模板,速度很重要,找到替换标记的索引,以便能够直接到达它们,而不必每次都从头开始。将“编译”抽象为具有良好属性的对象,它们只需要在更改模板后进行更新。
答案 12 :(得分:0)
Rythm现在发布了一个名为 String interpolation mode 的新功能的java模板引擎,它允许您执行以下操作:
String result = Rythm.render("Hello @who!", "world");
以上情况表明您可以按位置将参数传递给模板。 Rythm还允许您按名称传递参数:
Map<String, Object> args = new HashMap<String, Object>();
args.put("title", "Mr.");
args.put("name", "John");
String result = Rythm.render("Hello @title @name", args);
由于您的模板内容相对较长,您可以将它们放入文件中,然后使用相同的API调用Rythm.render
:
Map<String, Object> args = new HashMap<String, Object>();
// ... prepare the args
String result = Rythm.render("path/to/my/template.xml", args);
注意Rythm将你的模板编译成java字节码,速度相当快,比String.format
快2倍
链接: