1

I have a string, and within it there will be combinations of [ ] [[ ]] ][ but I need to replace the single [ and ] with < and > but leave alone (don't match) anything that is between [[ ]].

I thought I could do this with a regex, but I'm really struggling to get it to work because the complexity is just beyond me at the moment.

Example string:

[a] [b]  <- should replace every [ with < and every ] with > so <a> <b>

[a][b]   <- should replace every [ with < and every ] with > so <a><b>

[[abc][a][b]]  <- should not replace anything. will always start with [[ and end with ]]

So thinking about this logically, I can do it in a loop with PHP but I really want to try and use a preg_replace if possible.

The logic, as far as I can decipher is to replace [ with < and ] with > EXCEPT between a [[ and ]] but I'm not sure if I can even do that in a regex. I can make it work partially by using lookahead/lookbehind but that still then matches [ and ] between [[ and ]] (e.g. [[ [a] ]].

So far I've got

    /(?<!(^|)\[)\[[^\]\[\[]*\]/gmi

Working to spot [a] but not [[a]] but fails if I have [[a [b] c]]. At this point I'm not worried about the replacement, I just need to get the regex working to match / not match.

5
  • 1
    Please take a look at how to format my code block Commented Apr 11, 2023 at 12:55
  • g as a modifier won't work in PHP. Please provide code you are using. Commented Apr 11, 2023 at 13:00
  • In the future you should provide regex101 links so we can work with what you are working with. I think regex101.com/r/PjCOes/1 achieves your goal Commented Apr 11, 2023 at 13:19
  • Or this one: \[([^][]*)\]|\[(?:[^][]*(?R)?)*+\](*SKIP)(*F) Commented Apr 11, 2023 at 13:27
  • If you don't have mixed strings, a \G based idea: \G[^][]*\K\[([^][]*)\] (this is a bit more efficient and worth to be mentioned if you don't have strings like e.g. [[a][b]] [a][b]) Commented Apr 12, 2023 at 7:20

1 Answer 1

0

You can use

preg_replace('~(\[\[(?:(?!\[\[|]]).|(?1))*]])(*SKIP)(*F)|\[([^][]*)]~s', '<$2>', $text)

See the PHP demo and the regex demo.

Details:

  • (\[\[(?:(?!\[\[|]]).|(?1))*]])(*SKIP)(*F) - Group 1: [[, zero or more occurrences of any char that is not a starting point of the [[ or ]] char sequences or the whole Group 1 pattern recursed, and then ]], and once the match is found, it is skipped, the new search starts at the failure location
  • | - or
  • \[([^][]*)] - a [, then zero or more chars other than [ and ] captured into Group 2, and then a ].
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you Wiktor, that is absolutely perfect and I now see how *SKIP works which I just couldn't get my head around.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.