Java相当于PHP的preg_replace_callback
我正在将应用程序从PHP移动到Java,并且在代码中大量使用正则expression式。 我已经在PHP中运行了一些似乎没有Java的东西:
preg_replace_callback()
对于正则expression式中的每一个匹配,它都会调用一个传递匹配文本作为参数的函数。 作为一个例子用法:
$articleText = preg_replace_callback("/\[thumb(\d+)\]/",'thumbReplace', $articleText); # ... function thumbReplace($matches) { global $photos; return "<img src=\"thumbs/" . $photos[$matches[1]] . "\">"; }
在Java中这样做的理想方式是什么?
重要提示 :正如Kip在注释中指出的那样,如果匹配正则expression式匹配replacestring,这个类将会有一个无限循环错误。 如果有必要的话,我会把它作为一个练习给读者来修复它。
我不知道任何类似于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);
请注意,您可以通过调用matchResults.group()
或matchResults.group(0)
来获取整个匹配的string,所以不必将callback传递给当前的string状态。
编辑:使它看起来更像PHPfunction的确切function。
这是原始的,因为提问者喜欢它:
public class CallbackMatcher { public static interface Callback { public void foundMatch(MatchResult matchResult); } private final Pattern pattern; public CallbackMatcher(String regex) { this.pattern = Pattern.compile(regex); } public String findMatches(String string, Callback callback) { final Matcher matcher = this.pattern.matcher(string); while(matcher.find()) { callback.foundMatch(matcher.toMatchResult()); } } }
对于这个特定的用例,最好简单地在callback中排队每个匹配,然后向后遍历它们。 这将防止在string被修改时必须重新映射索引。
试图仿效PHP的callback函数,当你可以在一个循环中使用appendReplacement()和appendTail()时,似乎是一个非常多的工作:
StringBuffer resultString = new StringBuffer(); Pattern regex = Pattern.compile("regex"); Matcher regexMatcher = regex.matcher(subjectString); while (regexMatcher.find()) { // You can vary the replacement text for each match on-the-fly regexMatcher.appendReplacement(resultString, "replacement"); } regexMatcher.appendTail(resultString);
我对这里的任何解决scheme都不满意。 我想要一个无状态的解决scheme。 而且如果我的replacestring恰好匹配模式,我不想在无限循环中结束。 当我在它时,我添加了对limit
参数和返回的count
参数的支持。 (我使用AtomicInteger
来模拟按引用传递整数)。我将callback
参数移动到参数列表的末尾,以便更容易定义一个匿名类。
这里是一个使用的例子:
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);
这里是我的版本的ReplaceCallback类:
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(); } }
我发现jdmichal的答案将无限循环,如果你的返回的string可以再次匹配; 下面是一个修改,防止这种匹配的无限循环。
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; }
这是我对你的build议做的最后结果。 如果有人遇到同样的问题,我觉得在这里出去会很高兴。 生成的调用代码如下所示:
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); } }