Java相当于PHP';s preg_replace_回调
我正在将应用程序从PHP迁移到Java,代码中大量使用正则表达式。我在PHP中遇到了一些似乎没有java等价物的东西:Java相当于PHP';s preg_replace_回调,java,php,regex,preg-replace,Java,Php,Regex,Preg Replace,我正在将应用程序从PHP迁移到Java,代码中大量使用正则表达式。我在PHP中遇到了一些似乎没有java等价物的东西: preg_replace_callback() 对于正则表达式中的每个匹配项,它调用一个函数,该函数将匹配文本作为参数传递。作为示例用法: $articleText = preg_replace_callback("/\[thumb(\d+)\]/",'thumbReplace', $articleText); # ... function thumbReplace($mat
preg_replace_callback()
对于正则表达式中的每个匹配项,它调用一个函数,该函数将匹配文本作为参数传递。作为示例用法:
$articleText = preg_replace_callback("/\[thumb(\d+)\]/",'thumbReplace', $articleText);
# ...
function thumbReplace($matches) {
global $photos;
return "<img src=\"thumbs/" . $photos[$matches[1]] . "\">";
}
$articleText=preg\u replace\u回调(“/\[thumb(\d+)\]/”,'thumbplace',$articleText);
# ...
函数thumbplace($matches){
全球$照片;
返回“”;
}
在Java中实现这一点的理想方法是什么?重要的:正如在注释中指出的,如果匹配的正则表达式与替换字符串匹配,则该类有一个无限循环错误。如果必要的话,我将把它作为练习留给读者来解决
我不知道Java内置了什么类似的东西。您可以使用Matcher类轻松推出自己的产品:
import java.util.regex.*;
public class CallbackMatcher
{
public static interface Callback
{
public String foundMatch(MatchResult matchResult);
}
private final Pattern pattern;
public CallbackMatcher(String regex)
{
this.pattern = Pattern.compile(regex);
}
public String replaceMatches(String string, Callback callback)
{
final Matcher matcher = this.pattern.matcher(string);
while(matcher.find())
{
final MatchResult matchResult = matcher.toMatchResult();
final String replacement = callback.foundMatch(matchResult);
string = string.substring(0, matchResult.start()) +
replacement + string.substring(matchResult.end());
matcher.reset(string);
}
}
}
然后打电话:
final CallbackMatcher.Callback callback = new CallbackMatcher.Callback() {
public String foundMatch(MatchResult matchResult)
{
return "<img src=\"thumbs/" + matchResults.group(1) + "\"/>";
}
};
final CallbackMatcher callbackMatcher = new CallbackMatcher("/\[thumb(\d+)\]/");
callbackMatcher.replaceMatches(articleText, callback);
对于这个特定的用例,最好只是将回调中的每个匹配项排队,然后向后运行。这将避免在修改字符串时必须重新映射索引。以下是我对您的建议所做操作的最终结果。我想如果有人也有同样的问题的话,在这里呆着会很好。生成的调用代码如下所示:
content = ReplaceCallback.find(content, regex, new ReplaceCallback.Callback() {
public String matches(MatchResult match) {
// Do something special not normally allowed in regex's...
return "newstring"
}
});
整个类列表如下所示:
import java.util.regex.MatchResult;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import java.util.Stack;
/**
* <p>
* Class that provides a method for doing regular expression string replacement by passing the matched string to
* a function that operates on the string. The result of the operation is then used to replace the original match.
* </p>
* <p>Example:</p>
* <pre>
* ReplaceCallback.find("string to search on", "/regular(expression/", new ReplaceCallback.Callback() {
* public String matches(MatchResult match) {
* // query db or whatever...
* return match.group().replaceAll("2nd level replacement", "blah blah");
* }
* });
* </pre>
* <p>
* This, in effect, allows for a second level of string regex processing.
* </p>
*
*/
public class ReplaceCallback {
public static interface Callback {
public String matches(MatchResult match);
}
private final Pattern pattern;
private Callback callback;
private class Result {
int start;
int end;
String replace;
}
/**
* You probably don't need this. {@see find(String, String, Callback)}
* @param regex The string regex to use
* @param callback An instance of Callback to execute on matches
*/
public ReplaceCallback(String regex, final Callback callback) {
this.pattern = Pattern.compile(regex);
this.callback = callback;
}
public String execute(String string) {
final Matcher matcher = this.pattern.matcher(string);
Stack<Result> results = new Stack<Result>();
while(matcher.find()) {
final MatchResult matchResult = matcher.toMatchResult();
Result r = new Result();
r.replace = callback.matches(matchResult);
if(r.replace == null)
continue;
r.start = matchResult.start();
r.end = matchResult.end();
results.push(r);
}
// Improve this with a stringbuilder...
while(!results.empty()) {
Result r = results.pop();
string = string.substring(0, r.start) + r.replace + string.substring(r.end);
}
return string;
}
/**
* If you wish to reuse the regex multiple times with different callbacks or search strings, you can create a
* ReplaceCallback directly and use this method to perform the search and replace.
*
* @param string The string we are searching through
* @param callback A callback instance that will be applied to the regex match results.
* @return The modified search string.
*/
public String execute(String string, final Callback callback) {
this.callback = callback;
return execute(string);
}
/**
* Use this static method to perform your regex search.
* @param search The string we are searching through
* @param regex The regex to apply to the string
* @param callback A callback instance that will be applied to the regex match results.
* @return The modified search string.
*/
public static String find(String search, String regex, Callback callback) {
ReplaceCallback rc = new ReplaceCallback(regex, callback);
return rc.execute(search);
}
}
import java.util.regex.MatchResult;
导入java.util.regex.Pattern;
导入java.util.regex.Matcher;
导入java.util.Stack;
/**
*
*类,该类通过将匹配的字符串传递给
*对字符串进行操作的函数。然后使用操作的结果替换原始匹配。
*
*示例:
*
*查找(“要搜索的字符串”,“/regular(expression/”,new ReplaceCallback.Callback(){
*公共字符串匹配(匹配结果匹配){
*//查询数据库或其他什么。。。
*返回match.group().replaceAll(“第二级替换”,“诸如此类”);
* }
* });
*
*
*实际上,这允许进行第二级的字符串正则表达式处理。
*
*
*/
公共类替换回调{
公共静态接口回调{
公共字符串匹配(匹配结果匹配);
}
私人最终模式;
私有回调;
私有类结果{
int启动;
内端;
字符串替换;
}
/**
*您可能不需要它。{@see find(String,String,Callback)}
*@param regex要使用的字符串regex
*@param callback在匹配项上执行的回调实例
*/
公共替换回调(字符串正则表达式,最终回调){
this.pattern=pattern.compile(regex);
this.callback=回调;
}
公共字符串执行(字符串){
final Matcher Matcher=this.pattern.Matcher(字符串);
堆栈结果=新堆栈();
while(matcher.find()){
final MatchResult MatchResult=matcher.toMatchResult();
结果r=新结果();
r、 replace=callback.matches(匹配结果);
如果(r.replace==null)
继续;
r、 start=matchResult.start();
r、 end=matchResult.end();
结果:push(r);
}
//使用stringbuilder改进此功能。。。
而(!results.empty()){
Result r=results.pop();
string=string.substring(0,r.start)+r.replace+string.substring(r.end);
}
返回字符串;
}
/**
*如果希望使用不同的回调或搜索字符串多次重用正则表达式,可以创建
*ReplaceCallback,并使用此方法执行搜索和替换。
*
*@param string我们正在搜索的字符串
*@param callback将应用于正则表达式匹配结果的回调实例。
*@返回修改后的搜索字符串。
*/
公共字符串执行(字符串字符串,最终回调){
this.callback=回调;
返回执行(字符串);
}
/**
*使用此静态方法执行正则表达式搜索。
*@param搜索我们正在搜索的字符串
*@param regex要应用于字符串的正则表达式
*@param callback将应用于正则表达式匹配结果的回调实例。
*@返回修改后的搜索字符串。
*/
公共静态字符串查找(字符串搜索、字符串正则表达式、回调){
ReplaceCallback rc=新的ReplaceCallback(regex,callback);
返回rc.execute(search);
}
}
当您可以在循环中使用appendReplacement()和appendTail()时,尝试模拟PHP的回调功能似乎需要做大量的工作:
public String replaceMatches(String string, Callback callback) {
String result = "";
final Matcher matcher = this.pattern.matcher(string);
int lastMatch = 0;
while(matcher.find())
{
final MatchResult matchResult = matcher.toMatchResult();
final String replacement = callback.foundMatch(matchResult);
result += string.substring(lastMatch, matchResult.start()) +
replacement;
lastMatch = matchResult.end();
}
if (lastMatch < string.length())
result += string.substring(lastMatch);
return result;
}
我发现,如果返回的字符串可以再次匹配,jdmichal的答案将是无限循环;下面是一个修改,它阻止无限循环进行这种匹配
final Map<String,String> props = new HashMap<String,String>();
props.put("MY_NAME", "Kip");
props.put("DEPT", "R&D");
props.put("BOSS", "Dave");
String subjectString = "Hi my name is ${MY_NAME} and I work in ${DEPT} for ${BOSS}";
String sRegex = "\\$\\{([A-Za-z0-9_]+)\\}";
String replacement = ReplaceCallback.replace(sRegex, subjectString, new ReplaceCallback.Callback() {
public String matchFound(MatchResult match) {
String group1 = match.group(1);
if(group1 != null && props.containsKey(group1))
return props.get(group1);
return match.group();
}
});
System.out.println("replacement: " + replacement);
公共字符串替换匹配(字符串、回调){
字符串结果=”;
final Matcher Matcher=this.pattern.Matcher(字符串);
int lastMatch=0;
while(matcher.find())
{
final MatchResult MatchResult=matcher.toMatchResult();
最终字符串替换=callback.foundMatch(matchResult);
结果+=字符串.substring(lastMatch,matchResult.start())+
替换
lastMatch=matchResult.end();
}
if(lastMatch
我对这里的任何解决方案都不太满意。我想要一个无状态的解决方案。如果我的替换字符串恰好与模式匹配,我也不想以无限循环结束。在这期间,我添加了对limit
参数和返回的count
参数的支持。(我使用了AtomicIn。)
public String replaceMatches(String string, Callback callback) {
String result = "";
final Matcher matcher = this.pattern.matcher(string);
int lastMatch = 0;
while(matcher.find())
{
final MatchResult matchResult = matcher.toMatchResult();
final String replacement = callback.foundMatch(matchResult);
result += string.substring(lastMatch, matchResult.start()) +
replacement;
lastMatch = matchResult.end();
}
if (lastMatch < string.length())
result += string.substring(lastMatch);
return result;
}
final Map<String,String> props = new HashMap<String,String>();
props.put("MY_NAME", "Kip");
props.put("DEPT", "R&D");
props.put("BOSS", "Dave");
String subjectString = "Hi my name is ${MY_NAME} and I work in ${DEPT} for ${BOSS}";
String sRegex = "\\$\\{([A-Za-z0-9_]+)\\}";
String replacement = ReplaceCallback.replace(sRegex, subjectString, new ReplaceCallback.Callback() {
public String matchFound(MatchResult match) {
String group1 = match.group(1);
if(group1 != null && props.containsKey(group1))
return props.get(group1);
return match.group();
}
});
System.out.println("replacement: " + replacement);
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.*;
public class ReplaceCallback
{
public static interface Callback {
/**
* This function is called when a match is made. The string which was matched
* can be obtained via match.group(), and the individual groupings via
* match.group(n).
*/
public String matchFound(MatchResult match);
}
/**
* Replaces with callback, with no limit to the number of replacements.
* Probably what you want most of the time.
*/
public static String replace(String pattern, String subject, Callback callback)
{
return replace(pattern, subject, -1, null, callback);
}
public static String replace(String pattern, String subject, int limit, Callback callback)
{
return replace(pattern, subject, limit, null, callback);
}
/**
* @param regex The regular expression pattern to search on.
* @param subject The string to be replaced.
* @param limit The maximum number of replacements to make. A negative value
* indicates replace all.
* @param count If this is not null, it will be set to the number of
* replacements made.
* @param callback Callback function
*/
public static String replace(String regex, String subject, int limit,
AtomicInteger count, Callback callback)
{
StringBuffer sb = new StringBuffer();
Matcher matcher = Pattern.compile(regex).matcher(subject);
int i;
for(i = 0; (limit < 0 || i < limit) && matcher.find(); i++)
{
String replacement = callback.matchFound(matcher.toMatchResult());
replacement = Matcher.quoteReplacement(replacement); //probably what you want...
matcher.appendReplacement(sb, replacement);
}
matcher.appendTail(sb);
if(count != null)
count.set(i);
return sb.toString();
}
}
replace(Pattern.compile("cat"), mr -> "dog", "one cat two cats in the yard")
Pattern.compile("random number")
.matcher("this is a random number")
.replaceAll(r -> "" + ThreadLocalRandom.current().nextInt())
this is a -107541873
Patern.compile("regex").matcher("some string")
.replaceAll(matchResult -> "something" + matchResult.group());