1

If I have a string of the form:

$text='remove1  (keep1) remove2 (keep2) remove3';

Using the response

https://stackoverflow.com/a/5293343/1154853

$re = '/[^()]*+(\((?:[^()]++|(?1))*\))[^()]*+/';
$text = preg_replace($re, '$1', $text);

I get

(keep1)(keep2)

If I want to go from

$text='remove1  {keep1} remove2 {keep2} remove3';

to

{keep1}{keep2}

that is I want to change the delimiters, how do I change the given regular expression? I tried all sorts of combinations of changing ( to { but I couldn't get it to work. I find these reg exp pretty tough!

Community
  • 1
  • 1
Geoff
  • 925
  • 4
  • 14
  • 36

3 Answers3

1

You need to escape the curly brackets:

$re = '/[^()]*+(\((?:[^()]++|(?1))*\))[^()]*+/';
          ^^     ^     ^^           ^   ^^
                 Esc                Esc

The markings are where your delimiters are, only two need to be escaped with slashes. I believe this will work:

$re = '/[^{}]*+(\{(?:[^{}]++|(?1))*\})[^{}]*+/';
nickb
  • 59,313
  • 13
  • 108
  • 143
  • This does the job. Now if I generalise and want to keep everything between `remove1 \hello{keep} remove2` such that I get `\hello{keep}` what do I need to edit then? – Geoff Mar 11 '12 at 15:03
0

For parentheses (), you could use:

$re = '/(?:^|(?<=\)))[^(]+/';
$text = preg_replace($re, '', $text);

EDIT For braces {}, use this variant:

$re = '/(?:^|(?<=\}))[^{]+/';
$text = preg_replace($re, '', $text);

Note that these will fail - as all regex does - for nested structures.

Explanation

(?:        # non-capturing group start
  ^        #   start-of-string
  |        #   or...
  (?<=\})  #   look-behind: a position preceded by a } closing brace
)          # end non-capturing group
[^{]+      # any character except an opening brace, multiple times

The non-capturing group fixes the start of the match behind a closing brace or at the start of the string. The [^{]+ matches every character up to the next brace. Effectively this matches everything in-between {words}.

See it: http://rubular.com/r/1Sp72TbHIi

EDIT #2 Of course, thinking less complicated, this would also work:

$re = '/((?:\\\w+)?\{[^}]*\})/';
$text = preg_replace($re, '$1', $text);

http://rubular.com/r/rPLMbCLbcq

Tomalak
  • 332,285
  • 67
  • 532
  • 628
  • When I test this, I don't get anything! Ah, I see, it works with ( and ) but I need { and }. – Geoff Mar 11 '12 at 14:54
  • Then why is your first sample using parenteses? – Tomalak Mar 11 '12 at 14:56
  • because that was the response I found elsewhere on stackoverflow, that worked for (text) but I couldn't get it to work for {text}. – Geoff Mar 11 '12 at 14:57
  • Ah, my bad. Sorry. :) I've added variant #2. – Tomalak Mar 11 '12 at 14:59
  • This works! If I want to generalise the delimiters such that `remove1\hello{keep1}remove2` goes to `\hello{keep}` where do I need to add the string \hello? – Geoff Mar 11 '12 at 15:17
  • @Geoff you know, you could have made that part of the question. Instead of `[^{]+` use `(?:(?!(?:\\\w+)?\{).)+`. No explanation for this one though, sorry. http://rubular.com/r/ClUy2nsVMI – Tomalak Mar 11 '12 at 17:55
  • I thought I'd ask a simpler question, and try to complete the rest myself. all the same, your code works. – Geoff Mar 11 '12 at 18:10
  • @Geoff Asking complete questions is preferred. Next time. :) – Tomalak Mar 11 '12 at 18:14
  • 1
    let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/8760/discussion-between-geoff-and-tomalak) – Geoff Mar 11 '12 at 19:32
  • @Geoff I've added a much less complicated version, see modified answer. – Tomalak Mar 12 '12 at 06:28
0

Try this:

$re = '/[^{}]*+(\{(?:[^{}]++|(?1))*\})[^{}]*+/';
$text = preg_replace($re, '$1', $text);
Ethan
  • 873
  • 1
  • 8
  • 11