3

By Curiosity, is there a way to write this with Java Stream ?

private final static Pattern decodePattern = Pattern.compile("&#(\\d+);");

StringBuffer buf = new StringBuffer();

Matcher m = decodePattern.matcher(somestring);
while (m.find()) {
    m.appendReplacement(buf, String.valueOf((char) Integer.parseInt(m.group(1))));
}
m.appendTail(buf);

String decodeString = buf.toString();
Pshemo
  • 122,468
  • 25
  • 185
  • 269
lvr123
  • 524
  • 6
  • 24
  • 2
    Matcher doesn't provide any additional functionality which creates stream of matches (although most likely it will be added in Java 9: http://download.java.net/java/jdk9/docs/api/java/util/regex/Matcher.html#results--, also `appendReplacement/Tail` will now work with `StringBuilder`). Anyway, maybe you are interested in these questions: http://stackoverflow.com/questions/37442358/match-a-pattern-and-write-the-stream-to-a-file-using-java-8-stream, http://stackoverflow.com/questions/28148483/how-do-i-create-a-stream-of-regex-matches. – Pshemo Oct 09 '16 at 20:16
  • Great news ! Thanks. – lvr123 Oct 09 '16 at 22:16

1 Answers1

2

Java 9

Cleaner code could be achieved with overloaded in Java 9 method from Matcher class:
public String replaceAll​(Function<MatchResult,String> replacer). Basically we can now replace code like

StringBuffer sb = new StringBuffer();
while(matcher.find()){
    matcher.appendReplacement(sb, /*create replacement*/);
}
matcher.appendTail(sb);
String result = sb.toString;

with

String replaced = matcher.replaceAll(match -> /*create replacement*/);

For example

String replaced = Pattern.compile("\\b\\w")
                         .matcher("foo bar baz")
                         .replaceAll(match -> match.group().toUpperCase());
//replaced: "Foo Bar Baz"

There was also added support for stream of elements matching pattern: public Stream<MatchResult> results​(), but IMO your loop doesn't look like good candidate for streams. Even with results() your code would look like:

//BTW Java 9 provides support for StringBuilder beside StringBuffer 
//for appendReplacement/appendTail

Matcher matcher = ...
StringBuilder buf = new StringBuilder(); 

matcher.results()
       .map(result -> String.valueOf((char) Integer.parseInt(result.group(1))) )
       .forEach(replacement -> matcher.appendReplacement(buf, replacement));
matcher.appendTail(buf);

String decodeString = buf.toString();

so it doesn't look much cleaner.


Java 8

In Java 8 Pattern and Matcher classes didn't change much in terms of stream support. Only Pattern received public Stream<String> splitAsStream(CharSequence input) method, but it creates stream of elements where pattern represents delimiter on which we want to split, not text which we want to find.

If you want to simplify your code in Java 8, write your own method in which you will supply Matcher and function which will map matched content (preferably represented by MatchResult or Matcher so it could access group(...) methods) to replacement which should be put instead of it.

Such method could look like:

public static String replaceMatches(Matcher m, Function<MatchResult, String> mapping){
    
    StringBuffer sb = new StringBuffer();
    while(m.find()){
        MatchResult matchResult = m.toMatchResult();
        m.appendReplacement(sb, mapping.apply(matchResult));
    }
    m.appendTail(sb);
    
    return sb.toString();
}

and you could use it like:

Pattern p = Pattern.compile("\\b\\w");
Matcher m = p.matcher("foo bar baz");

String result = replaceMatches(m, mr -> mr.group().toUpperCase());

System.out.println(result);

Result: Foo Bar Baz.

Community
  • 1
  • 1
Pshemo
  • 122,468
  • 25
  • 185
  • 269
  • Your example would be simpler and more efficient using `Pattern.compile("\\b\\w")` and `replaceMatches(m, mr -> mr.group().toUpperCase())`… – Holger Jan 09 '18 at 08:44