简体   繁体   中英

Java String replaceAll Recursive behavior

Yesterday I was working on customized letters and memos. And I have made mapping keywords, like: [Dateofbirth], [Email], [Employee], [Salary] etc, which will be replace at the time of generation.

Example: dear [Employee], your Current Salary is [Salary].

expected output:

Output: dear John, your Current Salary is 12000.

I am using replaceAll() method here is a code.

   String str = "dear [Employee], your Current Salary is [Salary].";
        Map<String,String> vals = new HashMap<String,String>();
        vals.put("[Employee]","John");
        vals.put("[Salary]","12000");
        for(String key:vals.keySet()){
            str=str.replaceAll(key,vals.get(key));
        }
        System.out.println(str);

but the out is:

dJohn1200012000 [JohnJohnJohnJohnJohnJohnJohnJohn], JohnJohnu12000 Cu1200012000Johnnt 1200012000John1200012000John is [1200012000John1200012000John].

I was confused and Googled it and tried to make it correct, after that I changed replaceAll() with replace()

EX: str=str.replace(key,vals.get(key));

Now it is working fine. The question is Why replace all is doing such kind of behavior what is the core concept of replaceAll() and When to use replace() when to use replaceAll() . Thanks

Before going to your problem, I hope you know about String#format that Java API have. With that you could simply write

String formattedString = String.format("dear %s, your Current Salary is %.2f", "John", 12000.45);
System.out.println(formattedString);

If you are doing that yourself for unavoidable situations, replaceAll treats the first parameter as a regex and not normal string to replace.

From docs of replaceAll.

Replaces each substring of this string that matches the given regular expression with the given replacement.

Since your parameter consists of [ and ] and they are matching with each character inside them.

It will work if you get rid of that [] and use any other special characters that are not meta characters for ex # .

    String str = "dear #Employee#, your Current Salary is #Salary#.";
    Map<String, String> vals = new HashMap<String, String>();
    vals.put("#Employee#", "John");
    vals.put("#Salary#", "12000");
    for (String key : vals.keySet()) {
        str = str.replaceAll(key, vals.get(key));
    }
    System.out.println(str);

gives you

dear John, your Current Salary is 12000.

Or simply use replace() as you said in the end.

Let us have a look at javadocs of replace and replaceAll methods. Go to javadocs

replace

public String replace(CharSequence target, CharSequence replacement)

Replaces each substring of this string that matches the literal target sequence with the specified literal replacement sequence. The replacement proceeds from the beginning of the string to the end, for example, replacing "aa" with "b" in the string "aaa" will result in "ba" rather than "ab".

So in simple words it replaces all the target words with replacement words.

replaceAll

public String replaceAll(String regex, String replacement)

Replaces each substring of this string that matches the given regular expression with the given replacement.

To understand the given behavior, it is important to understand how regex works. Regex is a world in its own, however, we can discuss the given regex here.

Square brackets group a set of characters together. When invoked any character in that set gets matched.

For example: [abc] will match all the a, b and c. Since replaceAll uses regex to match, it would replace all a, b and c with replacement string.

replaceAll() matches using a regular expression .
replace() matches using plain text.

The regex [Employee] is a character class that matches any single character that is either E , m , p , l , o , y or e .

replaceAll treats the first parameter as regex. So you need to escape the regex specific characters which in your case is [ and ]. They signify a set of characters hence the replaceAll is matching every character in that set and replacing with the value you provided. Try this instead to get the output you desire -

vals.put("\\[Employee\\]","John");
vals.put("\\[Salary\\]","12000");

Here we have escaped the special symbol [ and ] thus it no longer treats it as a set.

Working program - https://www.ideone.com/2hCbIM

ReplaceAll compiles a regular expression and applies to the string. In this case, since your Key has '[' , it would match any of the characters present inside that. Hence, for each occurrence, it has replaced with the value

replaceAll() expects a regular expression but replace() expects the exact string (char sequence). Regular expression requires its special characters escaped. So when it is required to simply replace a string use replace() method, and when it is required to match a regular expression and need to replace it, use replaceAll().

This link may be useful for you to understand it. Basically, there is only difference is replaceAll replaces all that strings which match regex provided as an argument and replace() replaces based on chars .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM