Могу ли я заменить группы в Java regex?
У меня есть этот код, и я хочу знать, могу ли я заменить только группы (не все шаблоны) в регулярном выражении Java.
Код:
//...
Pattern p = Pattern.compile("(d).*(d)");
String input = "6 example input 4";
Matcher m = p.matcher(input);
if (m.find()) {
//Now I want replace group one ( (d) ) with number
//and group two (too (d) ) with 1, but I don't know how.
}
6 ответов:
использовать
$n(где n-цифра) для ссылки на захваченные подпоследовательности вreplaceFirst(...). Я предполагаю, что вы хотели заменить первую группу литеральной строкой "номер" и вторая группа со значением первой группы.Pattern p = Pattern.compile("(\d)(.*)(\d)"); String input = "6 example input 4"; Matcher m = p.matcher(input); if (m.find()) { // replace first number with "number" and second number with the first String output = m.replaceFirst("number "); // number 46 }считают
(\D+)для второй группы вместо(.*).*является жадным matcher, и сначала будет потреблять последнюю цифру. Затем сопоставителю придется вернуться, когда он поймет финал(\d)не имеет ничего, чтобы соответствовать, прежде чем он может совпадать с последней цифрой.
вы могли бы использовать
Matcher#start(group)иMatcher#end(group)чтобы построить общий метод замены:public static String replaceGroup(String regex, String source, int groupToReplace, String replacement) { return replaceGroup(regex, source, groupToReplace, 1, replacement); } public static String replaceGroup(String regex, String source, int groupToReplace, int groupOccurrence, String replacement) { Matcher m = Pattern.compile(regex).matcher(source); for (int i = 0; i < groupOccurrence; i++) if (!m.find()) return source; // pattern not met, may also throw an exception here return new StringBuilder(source).replace(m.start(groupToReplace), m.end(groupToReplace), replacement).toString(); } public static void main(String[] args) { // replace with "%" what was matched by group 1 // input: aaa123ccc // output: %123ccc System.out.println(replaceGroup("([a-z]+)([0-9]+)([a-z]+)", "aaa123ccc", 1, "%")); // replace with "!!!" what was matched the 4th time by the group 2 // input: a1b2c3d4e5 // output: a1b2c3d!!!e5 System.out.println(replaceGroup("([a-z])(\d)", "a1b2c3d4e5", 2, 4, "!!!")); }Регистрация онлайн демо здесь.
добавить третью группу, добавив parens вокруг
.*, затем замените подпоследовательность с"number" + m.group(2) + "1". например:String output = m.replaceFirst("number" + m.group(2) + "1");
Извините, что избили мертвую лошадь, но это немного странно, что никто не указал на это-"да, вы можете, но это противоположно тому, как вы используете захват групп в реальной жизни".
Если вы используете регулярное выражение так, как оно предназначено для использования, решение так же просто, как это:
"6 example input 4".replaceAll("(?:\d)(.*)(?:\d)", "number");или, как справедливо указал шмосель ниже,
"6 example input 4".replaceAll("\d(.*)\d", "number");...поскольку в вашем регулярном выражении нет никакой веской причины группировать десятичные числа вообще.
вы обычно не используете захват группы на части строки, которую вы хотите удалить, вы используете их на части строки, вы хотите сохранить.
Если вы действительно хотите группы, которые вы хотите заменить, то, что вы, вероятно, хотите вместо этого шаблонный движок (например, усы, ejs, StringTemplate,...).
в стороне от любопытных, даже не захватывающие группы в регулярных выражениях существуют только в том случае, если они нужны движку регулярных выражений распознавать и пропускать текст переменной. Например, в
(?:abc)*(capture me)(?:bcd)*они вам нужны, если ваш вход может выглядеть либо как " abcabcзахватить меняbcdbcd " или " abcзахватить меняbcd "или даже просто "захватите меня".
или наоборот: если текст всегда один и тот же, и вы его не фиксируете, нет никаких причин использовать группы вообще.
вы можете использовать matcher.start () и matcher.end () методы для получения групповых позиций. Поэтому с помощью этой позиции вы можете легко заменить любой текст.
вот другое решение, которое также позволяет заменить одну группу в нескольких матчах. Он использует стеки для отмены порядка выполнения, поэтому строковая операция может быть безопасно выполнена.
private static void demo () { final String sourceString = "hello world!"; final String regex = "(hello) (world)(!)"; final Pattern pattern = Pattern.compile(regex); String result = replaceTextOfMatchGroup(sourceString, pattern, 2, world -> world.toUpperCase()); System.out.println(result); // output: hello WORLD! } public static String replaceTextOfMatchGroup(String sourceString, Pattern pattern, int groupToReplace, Function<String,String> replaceStrategy) { Stack<Integer> startPositions = new Stack<>(); Stack<Integer> endPositions = new Stack<>(); Matcher matcher = pattern.matcher(sourceString); while (matcher.find()) { startPositions.push(matcher.start(groupToReplace)); endPositions.push(matcher.end(groupToReplace)); } StringBuilder sb = new StringBuilder(sourceString); while (! startPositions.isEmpty()) { int start = startPositions.pop(); int end = endPositions.pop(); if (start >= 0 && end >= 0) { sb.replace(start, end, replaceStrategy.apply(sourceString.substring(start, end))); } } return sb.toString(); }
Comments