0

I have a string that looks like the following:

"This is a test [Text that (cannot) be changed]. But (this) can be changed."

I want to replace the strings inside ( and ) with html but not when they are inside [ ]. I want to replace all text within [ ] with a different html. My final result would look like the following.

"This is a test <p>Text that (cannot) be changed</p>". But <b>this</b> can be changed."

I created an expression that could select everything outside the [ ] strings. But how can I perform replace to this selected text only? To select everything outside [ ] I use this:

([^\[\]]+)(?:\s|$|\[)

This selects all text outside [ and ]. I want to perform regex replace for ( ) on this selected text only.

pewpewlasers
  • 3,025
  • 4
  • 31
  • 58
  • Do you always want to replace `[...]` by `

    ...

    `, and `(...)` by `...`? (Except when `(...)` is inside `[...]`)
    – Sébastien Jan 27 '15 at 11:32

4 Answers4

2

You might combine a regex and a callback function to replace the stuff you want:

var subject = 'This (is) a test [Then some text that (cannot) (be) changed]. But (this) (can) be changed.';
var regex = /(?:^|])([^\[]*)(?:\n|$|\[)/g;

var replace = subject.replace(regex, function(match, p1)
{
    return match.replace(/\(/g, '<b>').replace(/\)/g, '</b>');
});

console.log(replace);
// This <b>is</b> a test [Then some text that (cannot) (be) changed]. But <b>this</b> <b>can</b> be changed.

Demo: http://jsfiddle.net/q21sns3s/2/

Regex explanation:

(?:^|]): we need the beginning of the subject or a closing ]

([^\[]*): followed by anything but an opening [

(?:\n|$|\[): ended by an opening [, a new line or the end of the subject ($)

User_2992644
  • 201
  • 1
  • 5
  • 2
    Good method. Only drawback to this is that it could lead to open tags. For example if your text is `var subject = 'This (is) a test [Then some text that (cannot) (be) changed]. But (this) (can be changed.';`. Notice the unclosed bracket on the last `can`. This leads to not being ended with – pewpewlasers Jan 27 '15 at 14:10
2

Best approach here is explained in this SO answer where you use a don't catch this|(do catch this) technique. My regex is this:

\[[^\]]*]|\(([^)]*)\)

Regular expression visualization

Debuggex Demo

So I catch everything between [] as well as everything between (), but only the latter generates a capture-group with the text you wanna keep. I can then examine this capture-group to decide what to do: return it unchanged or put <b></b> around it.

var subject = 'This (is) a test [Then some text that (cannot) (be) changed]. But (this) can (be) changed.';
var regex = /\[[^\]]*]|\(([^)]*)\)/g;

var replace = subject.replace(regex, function(match, p1)
{
    return (p1==undefined)?match:'<b>'+p1+'</b>';
});

console.log(replace);
// This <b>is</b> a test [Then some text that (cannot) (be) changed]. But <b>this</b> can <b>be</b> changed.

(credit to @johansatge for the nice template, I just changed the regex and the return line)

Community
  • 1
  • 1
asontu
  • 4,548
  • 1
  • 21
  • 29
0

Using /[(][a-z]+[)]/g on the text you have extracted will allow you to replace the text "(this)"

var newText = myExtractedText.replace(/[(][a-z]+[)]/g, "(new text)"); 

EDIT:

To replace the text from the string initially (With out extracting the stuff inside the '[]' first, you can do:

var s = "This is a test [Text that (cannot) be changed]. But (this) can be changed.",
    match = s.match(/[a-z ]+([(][a-z]+[)])[a-z .]+$/ig)[0];

console.log(match.replace(/[(][a-z]+[)]/, '(new text)'));
atmd
  • 7,430
  • 2
  • 33
  • 64
  • This modifies the `(cannot)` text too. – Aran-Fey Jan 27 '15 at 11:37
  • @Rawing in the OP "I created an expression that could select everything outside the [ ] strings" so he has already extracted the text containing the area he wants to change ('myExtractedText') – atmd Jan 27 '15 at 11:53
  • It hasn't been extracted. The regex I put up simply selects the part of the text that is outside `[` and `]`. If you really do extract this you will get an array of matches, which is still difficult to be evaluated into a complete string i.e. `"This is a test

    Text that (cannot) be changed

    ". But this can be changed."`
    – pewpewlasers Jan 27 '15 at 13:02
  • updated the answer to add the initial part (ignoring the brackets in side the square brackets) – atmd Jan 27 '15 at 13:16
0

You could do sth. like this to capture only (..) which are not inside []. But Javascript lacks the lookbehind feature.

(?!\[)\(.*?\)(?<!\])

You could mimic this feature like described in here. However it think the answer of @funkwurm seem's much cleaner. It's the best way to go for a problem like this.

cb0
  • 8,415
  • 9
  • 52
  • 80