user1003757
user1003757

Reputation: 41

How to match one, but not two characters using regular expressions

Using javascript regular expressions, how do you match one character while ignoring any other characters that also match?

Example 1: I want to match $, but not $$ or $$$. Example 2: I want to match $$, but not $$$.

A typical string that is being tested is, "$ $$ $$$ asian italian"

From a user experience perspective, the user selects, or deselects, a checkbox whose value matches tags found in in a list of items. All the tags must be matched (checked) for the item to show.

    function filterResults(){

// Make an array of the checked inputs
var aInputs = $('.listings-inputs input:checked').toArray();
// alert(aInputs);
// Turn that array into a new array made from each items value.
var aValues = $.map(aInputs, function(i){
    // alert($(i).val());
    return $(i).val();
});
// alert(aValues);
// Create new variable, set the value to the joined array set to lower case.
// Use this variable as the string to test
var sValues = aValues.join(' ').toLowerCase();
// alert(sValues);

// sValues = sValues.replace(/\$/ig,'\\$');
// alert(sValues);

// this examines each the '.tags' of each item
$('.listings .tags').each(function(){
    var sTags = $(this).text();
    // alert(sTags);
    sSplitTags = sTags.split(' \267 '); // JavaScript uses octal encoding for special characters
    // alert(sSplitTags);
    // sSplitTags = sTags.split(' \u00B7 '); // This also works

    var show = true;

    $.each(sSplitTags, function(i,tag){

        if(tag.charAt(0) == '$'){
            // alert(tag);
            // alert('It begins with a $');
            // You have to escape special characters for the RegEx
            tag = tag.replace(/\$/ig,'\\$');
            // alert(tag);
        }           

        tag = '\\b' + tag + '\\b';

        var re = new RegExp(tag,'i');

        if(!(re.test(sValues))){
            alert(tag);
            show = false;
            alert('no match');
            return false;
        }
        else{
            alert(tag);
            show = true;
            alert('match');
        }
    });

    if(show == false){
        $(this).parent().hide();
    }
    else{
        $(this).parent().show();
    }

});

// call the swizzleRows function in the listings.js
swizzleList();
}

Thanks in advance!

Upvotes: 0

Views: 5026

Answers (4)

js2010
js2010

Reputation: 27443

I simplified down to this. You have to negate both sides. I'm using powershell, but it should work in javascript. In regex, the dollarsign has to be escaped with a backslash.

'hi $ hi' | select-string '[^\$]\$[^\$]'

hi $ hi


'hi $$ hi' | select-string '[^\$]\$[^\$]'

# nothing

Actually something more complicated is needed, if the dollar is the only thing on the line. (Ooo, regex101.com has a debugger.) I was doing this for '&' but not '&&' in .bat files, and '&' doesn't need a backslash.

'$' | select-string '(^|[^\$])\$($|[^\$])'

$


'$$' | select-string '(^|[^\$])\$($|[^\$])'

# nothing

Upvotes: 0

Wiktor Stribiżew
Wiktor Stribiżew

Reputation: 626870

Normally, with regex, you can use (?<!x)x(?!x) to match an x that is not preceded nor followed with x.

With the modern ECMAScript 2018+ compliant JS engines, you may use lookbehind based regex:

(?<!\$)\$(?!\$)

See the JS demo (run it in supported browsers only, their number is growing, check the list here):

const str ="$ $$ $$$ asian italian";
const regex = /(?<!\$)\$(?!\$)/g;
console.log( str.match(regex).length ); // Count the single $ occurrences
console.log( str.replace(regex, '<span>$&</span>') ); // Enclose single $ occurrences with tags
console.log( str.split(regex) ); // Split with single $ occurrences

Upvotes: 4

Lachezar
Lachezar

Reputation: 6703

Something like that:

q=re.match(r"""(x{2})($|[^x])""", 'xx')

q.groups() ('xx', '')

q=re.match(r"""(x{2})($|[^x])""", 'xxx')

q is None True

Upvotes: 0

Phil H
Phil H

Reputation: 20141

\bx\b

Explanation: Matches x between two word boundaries (for more on word boundaries, look at this tutorial). \b includes the start or end of the string.

I'm taking advantage of the space delimiting in your question. If that is not there, then you will need a more complex expression like (^x$|^x[^x]|[^x]x[^x]|[^x]x$) to match different positions possibly at the start and/or end of the string. This would limit it to single character matching, whereas the first pattern matches entire tokens.

The alternative is just to tokenize the string (split it at spaces) and construct an object from the tokens which you can just look up to see if a given string matched one of the tokens. This should be much faster per-lookup than regex.

Upvotes: 1

Related Questions