3

I am trying to replace a given character by a regular expression match. For example, given the following string:
If you look at what you have in life, you'll always have more. If you look at what you don't have in life, you'll never have enough
I would like to replace all 't' with a '!' only where the match is between the characters 'ok' and 'fe'.

I get the match between 'ok' and 'fe' with this regular expression:

(?<=ok).*?(?=fe)

And I can only match one character with the following regex:

(?<=ok).*?(t).*?(?=fe)

I tried to transform that regex in the following way but it does not work:

(?<=ok).*?((t).*?)*?(?=fe)

How can I match all 't' between 'ok' and 'fe'? https://regex101.com/r/ORgseA/1

Davide
  • 1,931
  • 2
  • 19
  • 39

1 Answers1

3

You can use

String result = text.replaceAll("(?s)(\\G(?!\\A)|ok)((?:(?!ok|fe|t).)*)t(?=(?:(?!ok|fe).)*fe)", "$1$2!");

See the regex demo and the Java demo:

String text = "If you look at what you have in life, you'll always have more. If you look at what you don't have in life, you'll never have enough";
String result = text.replaceAll("(?s)(\\G(?!\\A)|ok)((?:(?!ok|fe|t).)*)t(?=(?:(?!ok|fe).)*fe)", "$1$2!");
System.out.println(result);
// => If you look a! wha! you have in life, you'll always have more. If you look a! wha! you don'! have in life, you'll never have enough

Details:

  • (?s) - Pattern.DOTALL embedded flag option (to make . match line break chars)
  • (\G(?!\A)|ok) - Group 1 ($1): ok or the end of the previous successful match
  • ((?:(?!ok|fe|t).)*) - Group 2 ($2): any one char, zero or more occurrences, as many as possible, that does not start a ok, fe or t char sequence
  • t - a t char
  • (?=(?:(?!ok|fe).)*fe) - immediately to the right, there must be any single char, zero or more occurrences, as many as possible, that does not start ok or fe char sequences and then a fe substring.
Wiktor Stribiżew
  • 607,720
  • 39
  • 448
  • 563
  • 1
    Very nice with the `\G` ++ – The fourth bird May 17 '21 at 11:48
  • Wow awesome answer. Thank you so much. Now I just need to understand the "Details" part with a coffee :) – Davide May 17 '21 at 11:49
  • 1
    @Davide If you wonder what these `(?:(?!xxx).)*` mean, you can read up [here](https://stackoverflow.com/a/37343088/3832970). – Wiktor Stribiżew May 17 '21 at 11:54
  • After analysis, I would like to add the following thing. If we want to replace several characters for a '!', we only need to enclose all the characters we want to replace in a group, e.g. (t|w|h). If we don't enclose them in a group, characters outise the range 'ok' and 'fe' are also replaced. – Davide May 17 '21 at 13:27
  • 1
    @Davide Sure, but you should consider using a non-capturing group, they are used to match alternative patterns that we need to remove from a string, we only capture what we want to keep. And put single chars into a character class. So, in the case of `(t|w|h)`, use `[twh]`. If you had `(t|w|hd)` you could use `(?:hd|[tw])`. – Wiktor Stribiżew May 17 '21 at 13:31
  • Uh yes :) I absolutely agree – Davide May 17 '21 at 14:10