Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Missing parentheses with Regex

Am I correct in thinking that Regex can't be used to detect missing parentheses (because there is no way of counting pairs)? Using JavaScript I've about a thousand strings which have been truncated and need to be edited by hand. I was hoping to be able to narrow this list down to the ones that need attention using code. The strings can be thought of in the form of:

  • (this is fine and does not need attention)
  • This is also [fine]
  • This is bad( and needs to be edited
  • This [is (also) bad
  • as is this} bad
  • this string has no brackets of any kind but must also be considered

If this is not possible then I'll just have to write a function to look for bracket pairs. Thank you

like image 871
Ghoul Fool Avatar asked Jan 15 '13 09:01

Ghoul Fool


2 Answers

function isFine(str) {  
  return /[(){}\[\]]/.test( str ) && 
    ( str.match( /\(/g ) || '' ).length == ( str.match( /\)/g ) || '' ).length &&
    ( str.match( /\[/g ) || '' ).length == ( str.match( /]/g ) || '' ).length &&
    ( str.match( /{/g ) || '' ).length == ( str.match( /}/g ) || '' ).length;
}

Test

isFine('(this is fine and does not need attention)');                 // true
isFine('This is also [fine]');                                        // true
isFine('This is bad( and needs to be edited');                        // false
isFine('This [is (also) bad');                                        // false
isFine('as is this} bad');                                            // false
isFine('this string has no brackets but must also be considered');    // false

Note though, that this doesn't check bracket order, i.e. a)b(c would be deemed fine.

For the record, here is a function that checks for missing brackets and checks that each type is correctly balanced. It doesn't allow a)b(c, but it does allow (a[bc)d] as each type is checked individually.

function checkBrackets( str ) {
    var lb, rb, li, ri,
        i = 0,
        brkts = [ '(', ')', '{', '}', '[', ']' ];   
    while ( lb = brkts[ i++ ], rb = brkts[ i++ ] ) { 
        li = ri = 0;
        while ( li = str.indexOf( lb, li ) + 1 ) {
            if ( ( ri = str.indexOf( rb, ri ) + 1 ) < li ) {
                return false;
            }
        }
        if ( str.indexOf( rb, ri ) + 1 ) {
            return false;
        } 
    }
    return true;
}

Finally, further to Christophe's post, here is what seems the best solution to checking for missing brackets and checking that all are correctly balanced and nested:

function checkBrackets( str ) {
    var s;
    str = str.replace( /[^{}[\]()]/g, '' );
    while ( s != str ) { 
        s = str;
        str = str.replace( /{}|\[]|\(\)/g, '' )
    }
    return !str;
};

checkBrackets( 'ab)cd(efg' );        // false   
checkBrackets( '((a)[{{b}}]c)' );    // true   
checkBrackets( 'ab[cd]efg' );        // true   
checkBrackets( 'a(b[c)d]e' );        // false   
like image 154
MikeM Avatar answered Sep 21 '22 17:09

MikeM


You can't do the recursion in the regex itself, but you can always do it in JavaScript.

Here is an example:

// First remove non-brackets:
string=string.replace(/[^{}[\]()]/g,"");
// Then remove bracket pairs recursively
while (string!==oldstring) {
  oldstring=string;
  string=string.replace(/({}|\[\]|\(\))/g,"");
}

The remainder are the non-matching brackets.

Live demo: http://jsfiddle.net/3Njzv/

If you need to count the pairs, you can do the replacements one at a time and add a counter:

// First remove non-brackets:
string=string.replace(/[^{}[\]()]/g,"");

// Then remove bracket pairs recursively
var counter=-1;
while (string!==oldstring) {
  counter ++;
  oldstring=string;
  string=string.replace(/({}|\[\]|\(\))/,"");
}
like image 44
Christophe Avatar answered Sep 21 '22 17:09

Christophe