19

I have really weird error in my java code and can not figure out what is wrong.

Let's say I have this code:

private void test()
{
    String test1 = replace("1.25");
    String test2 = replace("1.5");
    String test3 = replace("1.75");
}

private String replace(String s)
{
     s = s.replaceAll(".25", "¼");
     s = s.replaceAll(".5", "½");
     s = s.replaceAll(".75", "¾");
     return s;
}

Then the result will be:

test1 = "¼"

test2 = "½"

test3 = "½" ??????????

Can someone please explain why test3 becomes "½"?

Paul Lo
  • 6,032
  • 6
  • 31
  • 36
Johan Nordli
  • 1,220
  • 3
  • 13
  • 26

7 Answers7

27

You're using replaceAll(), which takes a regular expression. In regex-land, . means "any character". Use replace() instead, which works with literal strings.

arshajii
  • 127,459
  • 24
  • 238
  • 287
  • 7
    Or use [`Pattern#quote`](http://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html#quote(java.lang.String)). – Maroun Dec 19 '13 at 15:59
8

Because replaceAll takes a regular expression. That means . is interpreted as a wildcard that also matches 7 so that .5 matches 75. You can escape in regular expressions using \ but note that this is also a String which means you will have to escape twice: so replaceAll("\\.5", "½") will do what you wanted.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
Martin Ring
  • 5,404
  • 24
  • 47
  • 1
    yes but in fact it's not false and not out off topic in that case, the least that can be done is no vote or upvote but downvote is definitly irrelevant and there is less than one minute between the two answer which make it makes a downvote even more innapropriate. – Kiwy Dec 19 '13 at 16:31
5

The problem is that in a regular expression . means any character. replaceAll takes a regex as a parameter so you should use some other means such as replace.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
Adam Arold
  • 29,285
  • 22
  • 112
  • 207
3

The replace all function interprets the first argument as a regular expression. ".5" as a regular expression will match anything followed by a 5, like your string test2. Escape the '.' character with a backslash:

 s = s.replaceAll("\\.25", "¼");
Slicedpan
  • 4,995
  • 2
  • 18
  • 33
3

replaceAll uses regex as first parameter that will need to be found and dot . in regex is metacharacter which will match all characters except new line. So when you are using

s = s.replaceAll(".5", "½");

on "1.75" .5 can match 75 (since . matches all). So after such change

1.75 
  -- will become 
1.½ 

(notice that 1.25 works correctly only because you used .25 before .5)

To prevent such behaviour you need to escape dot. You can do it by

  • placing \ before it (remember that to create \ literal you need to write it as "\\") "\\.",
  • place it in character class [.],
  • surround it with \Q and \E which represent start and end of quote \\Q.\\E
  • surrounding with \Q and \E can be also done with Pattern.quote(".")
  • In case of Pattern instance while compiling you can add Pattern.LITERAL flag to make all metacharacters used in pattern literals.

If you want our entire pattern be simple literal you can use replace instead of replaceAll which will automatically use Pattern.LITERAL flag and change all regex metacharacters into simple literals.

So you can try something like

return s.replaceAll("\\.25", "¼").replaceAll("\\.5", "½").replaceAll("\\.75", "¾");

or simpler

return s.replace(".25", "¼").replace(".5", "½").replace(".75", "¾");
Pshemo
  • 122,468
  • 25
  • 185
  • 269
1

The first parameter of replaceAll() is a REGEX, and '.' means any character in REGEX area.

Paul Lo
  • 6,032
  • 6
  • 31
  • 36
1

Use String.replace() instead of String.replaceAll()

Do like this

    private String replace(String s)
    {
        s = s.replace(".25", "¼");
        s = s.replace(".5", "½");
        s = s.replace(".75", "¾");
        return s;
    }
Prabhakaran Ramaswamy
  • 25,706
  • 10
  • 57
  • 64