Java Equivalent to PHP'S Preg_Replace_Callback

Java equivalent to PHP's preg_replace_callback

IMPORTANT: As pointed out by Kip in the comments, this class has an infinite loop bug if the matching regex matches on the replacement string. I'll leave it as an exercise to readers to fix it, if necessary.


I don't know of anything similar that's built into Java. You could roll your own without too much difficulty, using the Matcher class:

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);
}
}
}

Then call:

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);

Note that you can get the entire matched string by calling matchResults.group() or matchResults.group(0), so it's not necessary to pass the callback the current string state.

EDIT: Made it look more like the exact functionality of the PHP function.

Here's the original, since the asker liked it:

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());
}
}
}

For this particular use case, it might be best to simply queue each match in the callback, then afterwards run through them backwards. This will prevent having to remap indexes as the string is modified.

preg_replace_callback just take 5 parameter?

From the PHP manual:

Version 7.4.0 The flags parameter was added.

Upgrade your php version.


Ideally, you shouldn't be using a preg_ function to modify a valid html document. You should use a legitimate dom parser.

Regular expression to remove -

You can use this regex:

(?<=<TN>.*?)\-(?=.*?</TN>)

For Java:

your_string= your_string.replaceAll("(?sim)(?<=<TN>.*?)\\-(?=.*?</TN>)", "");

Java Regex Replace with Capturing Group

The definitive solution to this problem was posted by Elliott Hughes on his blog a couple years ago. Elliott keeps introducing pointless dependencies to other classes in the online version, so I'll post a stand-alone version here (the dependencies are only in the tests in the main() method).

import java.util.regex.*;

/**
* A Rewriter does a global substitution in the strings passed to its
* 'rewrite' method. It uses the pattern supplied to its constructor, and is
* like 'String.replaceAll' except for the fact that its replacement strings
* are generated by invoking a method you write, rather than from another
* string. This class is supposed to be equivalent to Ruby's 'gsub' when
* given a block. This is the nicest syntax I've managed to come up with in
* Java so far. It's not too bad, and might actually be preferable if you
* want to do the same rewriting to a number of strings in the same method
* or class. See the example 'main' for a sample of how to use this class.
*
* @author Elliott Hughes
*/
public abstract class Rewriter
{
private Pattern pattern;
private Matcher matcher;

/**
* Constructs a rewriter using the given regular expression; the syntax is
* the same as for 'Pattern.compile'.
*/
public Rewriter(String regex)
{
this.pattern = Pattern.compile(regex);
}

/**
* Returns the input subsequence captured by the given group during the
* previous match operation.
*/
public String group(int i)
{
return matcher.group(i);
}

/**
* Overridden to compute a replacement for each match. Use the method
* 'group' to access the captured groups.
*/
public abstract String replacement();

/**
* Returns the result of rewriting 'original' by invoking the method
* 'replacement' for each match of the regular expression supplied to the
* constructor.
*/
public String rewrite(CharSequence original)
{
this.matcher = pattern.matcher(original);
StringBuffer result = new StringBuffer(original.length());
while (matcher.find())
{
matcher.appendReplacement(result, "");
result.append(replacement());
}
matcher.appendTail(result);
return result.toString();
}



public static void main(String... args) throws Exception
{
String str = "12 54 1 65";

// anonymous subclass
Rewriter tripler = new Rewriter("(\\d{1,2})")
{
public String replacement()
{
int intValue = Integer.valueOf(group(1));
return String.valueOf(intValue * 3);
}
};
System.out.println(tripler.rewrite(str));

// inline subclass
System.out.println(new Rewriter("(\\d{1,2})")
{
public String replacement()
{
int intValue = Integer.valueOf(group(1));
return String.valueOf(intValue * 3);
}
}.rewrite(str));

}
}

Get what was removed by String.replaceAll()

You can use a Matcher with the append-and-replace procedure:

String regex = "\\d*";

Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(input);

StringBuffer sb = new StringBuffer();
StringBuffer replaced = new StringBuffer();
while(matcher.find()) {
replaced.append(matcher.group());
matcher.appendReplacement(sb, "");
}
matcher.appendTail(sb);

System.out.println(sb.toString()); // prints the replacement result
System.out.println(replaced.toString()); // prints what was replaced

Java: replace a set of characters with other different characters

It would be easier to first create some kind of mapping between replaced character and its replacement. I mean something like

Map<String, String> map = new HashMap<>();
map.put("a", "c");
map.put("b", "d");

then you can use appendReplacement and appendTail from Matcher class to replace matched character. Deciding on how to get replaced character can be done like map.get(matchedCharacter).

Simple Demo

Map<String, String> map = new HashMap<>();
map.put("a", "c");
map.put("b", "d");

String demo = "abcdef";

Pattern p = Pattern.compile("[ab]");
Matcher m = p.matcher(demo);

StringBuffer sb = new StringBuffer();
while (m.find()){
m.appendReplacement(sb, map.get(m.group()));
}
m.appendTail(sb);
String replaced = sb.toString();

System.out.println(replaced);

Output: cdcdef



UPDATE for Java 9 and above

In below template we usually change only one thing, decision about what to use as replacement of founded match.

StringBuffer sb = new StringBuffer();
while (m.find()){
m.appendReplacement(sb, /* decision about replacement*/ );
// ^^^^^^^^^^^^^^^^^^^^^^^^^^
}
m.appendTail(sb);
String replaced = sb.toString();

So since rest of that logic is repeated, this template was wrapped as String replaceAll​(Function<MatchResult,String> replacer) which require from us to provide logic of obtaining replacement for founded match. So above code can be reduced to:

Map<String, String> map = new HashMap<>();
map.put("a", "c");
map.put("b", "d");

String demo = "abcdef";

Pattern p = Pattern.compile("[ab]");
Matcher m = p.matcher(demo);

String replaced = m.replaceAll(match -> map.get(match.group()));
System.out.println(replaced);

Is it possible to switch between + and - using regex in Java?

You can use this trick :

String equation = "<Your equation>"
equation = equation.replaceAll("+","$$$");
equation = equation.replaceAll("-","+");
equation = equation.replaceAll("$$$","-");

Assuming $$$ is not in your equation.



Related Topics



Leave a reply



Submit