silverAndroid
silverAndroid

Reputation: 1050

Is there a way to replace a substring based on the number of occurrences?

I want to remove every occurrence of "*" but ignore "**", so for example, if I had "testing *single star* and **double star**", the sentence after removing would be "testing single star and **double star**".

I was thinking of using replace("*", "") but when I tried that, it got rid of all the stars.

What should I do?

Upvotes: 1

Views: 68

Answers (2)

Pshemo
Pshemo

Reputation: 124235

You could probably try with

yourString  = yourString.replaceAll("(\\*{2})|\\*","$1");

Explanation: replaceAll uses regular expression (regex) mechanisms. Also * is special character in regex so we need to escape it (I used \\* to do so, but you can also use [*] or surround it with \\Q \\E). Now lets see what this regex is:

  • (\\*{2}) - will try to match ** and place it in group 1
  • | - OR operator
  • \\* - match only one *

So this solution will try to first find **, store it in group 1 and later replace it with match from group 1 (so we are replacing it with itself which means they will stay in text).
But if regex engine will not be able to find ** and instead will will find *, group 1 will be empty since it can match only two stars (since that part of regex is surrounded with parenthesis). So our replacement $1 will represent empty string which means we will remove single *.

So current solution will cause these changes:

*     ->       - found 1 star (remove it)
**    -> **    - found 2 stars (stay)
***   -> **    - found 2 stars (stay), 1 star (remove)
****  -> ****  - found 2 stars (stay), 2 stars (stay)
***** -> ****  - found 2 stars (stay), 2 stars (stay) and 1 star (remove)

If you don't want to affect 2 or more stars (like *** should still be ***) instead of {2} use {2,}, this will allow us to store in group 1 two or more stars (but not one).

Upvotes: 3

Wiktor Stribiżew
Wiktor Stribiżew

Reputation: 626903

You can leverage lookarounds

String s = "testing *single star* and **double star**".replaceAll("(?<![*])[*](?![*])", "");

See IDEONE demo (result: testing single star and **double star**)

Putting * into a character class allows to match a literal asterisk without escaping.

The (?<![*]) is a negative lookbehind checking if there is no asterisk before an asterisk, and (?![*]) is a negative lookahead making sure there is no asterisk after the asterisk being matched.

Upvotes: 7

Related Questions