1

I want to replace certain substrings with a replacement, both defined within a Hashmap. An example is the following:

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

public class RegexTest {
    private static HashMap<String, String> conversionTable;

    public static void main(String[] args) {
        initConversionTable();
        System.out.println(convertNumbers("la1"));
        System.out.println(convertNumbers("la1, lb3"));
    }

    private static String convertNumbers(String text) {
        String regex = "([aub][1-3])";
        Matcher m = Pattern.compile(regex).matcher(text);
        while(m.find()) {
            text  = m.replaceAll(conversionTable.get(m.group()));
        }

        return text;
    }

    private static void initConversionTable() {
        conversionTable = new HashMap<>();
        conversionTable.put("a1", "A1");
        conversionTable.put("a2", "A2");
        conversionTable.put("a3", "A3");
        conversionTable.put("u1", "U1");
        conversionTable.put("u2", "U2");
        conversionTable.put("u3", "U3");
        conversionTable.put("b1", "B1");
        conversionTable.put("b2", "B2");
        conversionTable.put("b3", "B3");
    }
}

For input data

la1
la1, lb3

The expected result should be

lA1
lA1, lB3

but is

lA1
lA1, lA1

Until now I have not succeeded finding a solution with a Matcher. Is there such a solution? Of course I could go through each Entry of the HashMap and do the replacement, but I would like to solve it with a Matcher, because I fear that for a very long Hashmap and many strings this could influence the performance.

Thank you very much!

VLAZ
  • 26,331
  • 9
  • 49
  • 67
Rolch2015
  • 1,354
  • 1
  • 14
  • 20
  • As explained in accepted answer problem is that `replaceAll` replaces all occurrences of regex match first time it will `find()` which will prevent other matches and proper replacements. But to solve this problem don't use `replaceFirst`. Instead use proper tool to handle precisely this kind of scenarios when we want to one match with another dynamically determined replacement (like value based on key in map). So use `appendReplacement` and `appendTail` from Matcher class. – Pshemo Jun 21 '15 at 21:42
  • I closed your question as duplicate of question where you can see one of solutions where these methods are used. You can also take a look at other examples like http://stackoverflow.com/a/25081783/1393766, http://stackoverflow.com/a/23689796/1393766 – Pshemo Jun 21 '15 at 21:46

1 Answers1

1

The problem is that you use replaceAll method so this will happen.

Step 1:

la1
la1, lb3

will go into loop because m.Find() returns true. It will find the first pattern "la1". Now you will get the conversion from table which is "lA1" and replace ALL occurences of the pattern, which is both "la1" and "lb3". Then the loop will not iterate again because the output is already

lA1
lA1, lA1

To fix the problem you need to replace just one occurence with the coresponding value, otherwise you will replace all of them with the first one.

Kyborek
  • 1,519
  • 11
  • 20
  • Thank you for your explanation, the problem is now clear to me. I have replaced the line, in which the replacement takes places, with `text = text.replace(m.group(), conversionTable.get(m.group()));`. But why does `text = m.replaceFirst(conversionTable.get(m.group()));` not work, i.e. empty result? – Rolch2015 Jun 21 '15 at 20:23
  • 1
    Maybe you need to reset the matcher for the loop to properly evaluate again: https://docs.oracle.com/javase/8/docs/api/java/util/regex/Matcher.html#replaceFirst-java.lang.String- – Kyborek Jun 21 '15 at 20:27
  • I just saw it too and tried, but it did not work. Further reading should solve the problem, but I will stick now to my first solution, otherwise it will get to complicated. Thank you again :) – Rolch2015 Jun 21 '15 at 20:35