Что эквивалентно Regex-replace-with-function-оценке в Java 7?

Я ищу очень простой способ получить эквивалент чего-то вроде следующего кода JavaScript. То есть для каждого совпадения я хотел бы вызвать определенную функцию преобразования и использовать результат в качестве значения замены.

var res = "Hello World!".replace(/\S+/, function (word) {
    // Since this function represents a transformation,
    // replacing literal strings (as with replaceAll) are not a viable solution.
    return "" + word.length;
})
// res => "5 6"

Только.. в Java. И, предпочтительно, как "единственный метод" или "шаблон", который можно использовать повторно.

Ответ 1

Ваш ответ находится в документации Matcher # appendReplacement. Просто поместите вызов своей функции в цикл while.

[Метод appendReplacement] предназначен для использования в цикле вместе с методами appendTail и find. Следующий код, например, записывает одну собаку двух собак во дворе в поток стандартного вывода:

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

Ответ 2

При разрешении Java 8 вы можете использовать лямбда-выражения, чтобы JavaScript заменял:

String result = StringReplacer.replace("Hello World!", Pattern.compile("\\S+"), m -> ("" + m.group().length()));

StringReplacer.java:

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class StringReplacer {
    public static String replace(String input, Pattern regex, StringReplacerCallback callback) {
        StringBuffer resultString = new StringBuffer();
        Matcher regexMatcher = regex.matcher(input);
        while (regexMatcher.find()) {
            regexMatcher.appendReplacement(resultString, callback.replace(regexMatcher));
        }
        regexMatcher.appendTail(resultString);

        return resultString.toString();
    }
}

StringReplacerCallback.java:

import java.util.regex.Matcher;

public interface StringReplacerCallback {
    public String replace(Matcher match);
}

Источник: http://www.whitebyte.info/programming/string-replace-with-callback-in-java-like-in-javascript

Ответ 3

Начиная с Java 9 Matcher.replaceAll:

Pattern.compile("\\S+").matcher("Hello World!")
        .replaceAll(mr -> "" + mr.group().length());

Параметр со свободным названием mr - это MatchResult с такими методами доступа, как mr.group(1) или mr.end() - mr.start().

Ответ 4

Не знаю, каковы ваши точные требования, но что-то вроде этого может работать:

String res = "";
for (String piece : "hello world".split(" "))
  res += Integer.toString(piece.length()) + " ";

Конечно, есть и другие способы написать это и твики, которые могут быть сделаны в зависимости от требований (например, используйте более точный разделитель, чем пробел).

Для точной реализации вашего фрагмента можно использовать, например, StreamTokenizer с помощью StringReader и некоторых оберток для разбора разделителей и вставки их между подсчетами.