Java – Regular expressions don’t work in Android, but work fine in Java

Regular expressions don’t work in Android, but work fine in Java… here is a solution to the problem.

Regular expressions don’t work in Android, but work fine in Java

I have the following code:

String compact =  Pattern.compile(" *(\\{) *| *(\\}) *").matcher(" { { } } ")
                     .replaceAll("$1$2");

In Java, compact contains {{}} –

that’s what I want – but on Android, I get {null{nullnull}null} which drives me crazy. Am I doing something wrong?

The next line produces the same result on Android:

String compact =  " { { } } ".replaceAll(" *(\\{) *| *(\\}) *", "$1$2")

This is a online Java version for anyone who wants to play it.

If it helps, I’ll use jdk1.7.0_79 in Android Studio on Mac to compile for Android SDK 23.

Update: Using “\\s*(\\{)\\s*|\\s*(\\

})\\s*" has the same effect.

Solution

I can’t tell you why it behaves the

way in Java, but I can tell you why it behaves the way in Android. However, I can provide some clues about Java.

When you command Matcher#replaceAll(String), the class checks for find(), and if so, it replaces the original content in the input to match the replacement String you provided.

So far so good.

However, when the substitution is actually a representation of a group, a different route is taken. This class attempts to find the content corresponding to the group (see Matcher#appendEvaluated(StringBuffer, String)) and append(String) to the internal StringBuffer for internal use.

It does this by explicitly calling the method group(int). The behavior of this method in Java and Android is that it will return null if there is no object corresponding to the given group when matched. Note: Throws an exception when there is no match at all, not just when it can’t find a match for the group. For example, group(int) without find().

Finally, when you attach a null object, StringBuffer outputs “null” String (literally the word “null“). So, for each non-existent group you request, when there is a match, it appends the word “null” to it.

In the case of replaceAll, the algorithms used in Java are very different and have different outputs.

Without doing too much testing or fixing inefficiencies (sorry, just going through SO at 3am in December didn’t help me sort things out), I think you can achieve the same results in Android as you would with the results you get in Java, by doing so instead of replaceAll:

String input = " { { } } ";
Matcher matcher = Pattern.compile(" *(\\{) *| *(\\}) *").matcher(input);
while (matcher.find()) {
    String a = matcher.group(1);  $1
    String b = matcher.group(2);  $2
    String replacement = null;
    if (a != null && b != null) {
        replacement = a + b;
    } else if (a != null) {
        replacement = a;
    } else if (b != null) {
        replacement = b;
    }
    if (replacement != null) {
        input = input.replace(matcher.group(), replacement);
    }
}

If not, please let me know. I only did some rough tests (it was 3 a.m., after all).

Related Problems and Solutions