1

I am supposed to make a custom decorator so I can replace both in an input from console and from a file:

  1. A set of chars with a specific character (e.g. char[] x = {'a', 'b'} with char y = '*', so both a and b become *
  2. A set of chars with another set of paired chars (e.g. char[] x = {'a', 'b'} with char[] y = {'c', 'd'}, so a becomes c and b becomes d

What would be the best approach for it? I made the first one with a regular expression ( String replaceAll = s.replaceAll("(a|b)", String.valueOf(replacement)); ), but this wouldn't work for the second case. Is there a way to make the second case in one regex? Should I do a HashMap ?

Comforse
  • 2,027
  • 4
  • 25
  • 40

4 Answers4

2

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);
Pshemo
  • 122,468
  • 25
  • 185
  • 269
1

DjMike,

For the second one, what would be helpful during the replacement is to call a method that you infuse with the logic to replace different characters with different strings.

PHP has a great facility that does just this called preg_replace_callback(). The linked answer is to a question about the Java equivalent of preg_replace_callback()

Community
  • 1
  • 1
zx81
  • 41,100
  • 9
  • 89
  • 105
  • I was looking for the same preg_replace_callback() style implementation, since I know PHP very well, but this seems way too complicated at this time and I also implemented the solution I marked as an answer. Thank you for your effort anyway. I would have preffered the regex style, but it is what it is for now. Good to know for the future. – Comforse Apr 19 '14 at 21:20
  • Ha, we were on the same track. Glad you found a solution DJ, wishing you a fun weekend. :) – zx81 Apr 19 '14 at 21:34
1

Here you have, this work 100% right for any example ....

public static void main(String[] args) {
        String test = "My name is Baba";

        Character[] x = { 'a', 'b' };

        StringBuilder sb = new StringBuilder();
        sb.append("(");
        for (int i = 0; i < x.length; i++) {
            if (i == (x.length - 1)) {
                sb.append(x[i] + ")");
            } else {
                sb.append(x[i] + "|");
            }
        }

        System.out.println(sb.toString());

        Character y = 'c';
        Character[] y1 = { 'd', 'e' };

        if (y.getClass().isArray()) {
            for (int i = 0; i < y1.length; i++) {
                test = test.replaceAll(x[i].toString(), y1[i].toString());
            }
        } else {
            test = test.replaceAll(sb.toString(), String.valueOf(y.toString()));
        }

        System.out.println(test);
    }
Matej Špilár
  • 2,617
  • 3
  • 15
  • 28
  • 1
    Seems pretty good and simple solution. Although I have already chose the @Pashemo's answer and also implemented it, I will give you a plus for your effort. Thank you! – Comforse Apr 19 '14 at 21:18
1

There is a more faster way:

public static void main(String[] args) {
    String target      = "ab";
    String replacement = "**";

    char[] array = "abcde".toCharArray();
    for (int i = 0; i < array.length; i++) {
        int index = target.indexOf(array[i]);
        if (index != -1) {
            array[i] = replacement.charAt(index);
        }
    }

    System.out.println(array);
}
Paul Vargas
  • 41,222
  • 15
  • 102
  • 148