Convert human readable number range to Regex

2020-06-27 09:18发布

I have a table that is displayed using datatables, above each column I have an empty text form field that users can type in terms to filter on. This works fine on all text fields, and works ok on integer fields as well. I am doing some conversion for some terms such as if the user types in NULL or NOT NULL for example I convert that to the regex ^$ or .

I know regex is intended to search text strings but this is what datatables uses so thats why I am doing this. What I want is for users to be able to type in a value such as "x to y" and to be able to convert that to a regular expression. I cannot find a function that does this, if anyone knows of one please let me know.

Assuming a function doesn't already exist, assume that only positive integers will be searched, and say up to 7 digits. so 0 - 9,999,999 can be searched. Also the only way this is triggered is by the keyword to with spaces " to ".

so something like this to start:

function convertNumRangeRegex(s){

if(s.indexOf(" to ") != -1){
var range = s.split(" to ");
lowRange = Number(range[0]);
highRange = Number(range[1]);   

if(lowRange >= 0 && lowRange < 10 && highRange < 10){
        s = "^[" + lowRange + "-" + highRange + "]$";
}};


return s; 
};

This works with numbers 0-9, but expanding on this seems like it would get pretty ugly. I am up for any ides. Thanks.

2条回答
贼婆χ
2楼-- · 2020-06-27 09:38

Forward

3 years later I rediscovered this question, and had some time to solve the puzzle. I'm not entirely clear on why you'd want to use a regex, but I'm sure it has to do with improving database return performance by not forcing all possible results to the client where they'll be evaluated.

That said, I'm sure you have your reasons.

Explanation

This set of functions will construct a regular expression that will do the following:

  • validate a given number is between a given range of positive integer numbers
  • rejects numbers that are outside that range
  • requires the entire string to be numbers
  • works with any size numbers
  • allow the entire range to include the lower and upper numbers

General overview

The function funRegexRange does all the heavy lifting. By building a string that will match all numbers from 0 to UpperRange

The function funBuildRegexForRange then constructs the actual regex with a negative lookahead and a positive lookahead.

The resulting regex will then validate your number is between 0 and the UpperRange inclusive, and not between 0 and the LowerRange not inclusive.

The functions will allow either numbers or strings values, but does not validate the inputs are integers. Providing values which do not equate to integers will yield unpredictable results.

Examples

To get the regex for a range from 400 to 500:

re = funBuildRegexForRange( 400, 500, true ) 

By setting the last parameter to true you it'll show the various parts being constructed and the full regex.

[0-3][0-9]{2}, [0-9]{1,2}
[0-4][0-9]{2}, 500, [0-9]{1,2}
Full Regex = /^(?!(?:[0-3][0-9]{2}|[0-9]{1,2})$)(?=(?:[0-4][0-9]{2}|500|[0-9]{1,2})$)/

The resulting regex looks like

Regular expression visualization

Asking for a range between 400 - 999999999999 [twelve digits] returns this monster:

Full Regex = /^(?!(?:[0-3][0-9]{2}|[0-9]{1,2})$)(?=(?:[0-8][0-9]{11}|9[0-8][0-9]{10}|99[0-8][0-9]{9}|999[0-8][0-9]{8}|9999[0-8][0-9]{7}|99999[0-8][0-9]{6}|999999[0-8][0-9]{5}|9999999[0-8][0-9]{4}|99999999[0-8][0-9]{3}|999999999[0-8][0-9]{2}|9999999999[0-8][0-9]|99999999999[0-8]|999999999999|[0-9]{1,11})$)/

Javascript Code

Live Example: https://repl.it/CLd4/4

Full code:

function funRegexRange (UpperRange, Inclusive, Debug) {
    // this function will build a basic regex that will match a range of integers from 0 to UpperRange
    
    UpperRange += "" // convert the value to a string
    var ArrUpperRange = UpperRange.split("")
    var intLength = ArrUpperRange.length
    var LastNumber = ArrUpperRange[intLength]
    var AllSubParts = []
    var SubPortion = ""

    for (i = 0; i < intLength; i++) {
        Position = intLength - (i +1)
        if ( Position >= 2 ) { 
            Trailing = "[0-9]{" + Position + "}";
        } else if ( Position == 1 ) {
            Trailing = "[0-9]";
        } else {
            Trailing = "";
        }
        
        if ( ArrUpperRange[i] >= 2 ) {
            ThisRange = "[0-" + (ArrUpperRange[i] - 1) + "]"
        } else if ( ArrUpperRange[i] == 1 ) {
            ThisRange = "0"
        } else {
            ThisRange = ""
        }
    
        if ( Debug ) {  
        // console.log( "Pos='" + Position + "' i='" + i + "' char='" + ArrUpperRange[i] + "' ThisRange='" + ThisRange + "' Trailing='" + Trailing + "'")
        }
    
        if ( ThisRange === "" && Trailing !== "" ) {
            // no need to return the this as this will be matched by the future SubPortions
        } else {
            if ( Position === 0 && ThisRange ==="" && Trailing === "") {
            } else {
                AllSubParts.push( SubPortion + ThisRange + Trailing);
        }
        }
    SubPortion += ArrUpperRange[i]
    }
    
    // insert the last number if this it should be included in the range
    if ( Inclusive ) {
        AllSubParts.push(UpperRange)
    }
    
    // all all numbers that have less digits than the range
    if ( intLength - 1 >= 2 ) { 
        Trailing = "[0-9]{1," + ( intLength - 1 ) + "}";
    } else if ( intLength - 1 >= 1 ) {
        Trailing = "[0-9]";
    } else {
        Trailing = "";
    }
    
    // insert trailing into the output stream
    if ( Trailing ){
        AllSubParts.push( Trailing );
    }   
    
    if ( Debug ) {
        console.log(AllSubParts.join(", "));
    } 
    return AllSubParts.join("|");
} // end function funRegexRange


function funBuildRegexForRange ( Start, End, Debug ){
    var Regex = new RegExp("^(?!(?:" + funRegexRange (LowerRange, false, Debug) + ")$)(?=(?:" + funRegexRange (UpperRange, true, Debug) + ")$)" ,"" )   
    if ( Debug ) {
        console.log("Full Regex = " + Regex + "")
    }
    return Regex
}

var Debug = false;
var Inclusive = true;
var LowerRange = "400";
var UpperRange = "500";

// var re = funBuildRegexForRange( LowerRange, UpperRange, true ) 

if ( Debug ){
    for (Range = 0; Range < 13; Range++) {
        console.log ("funRegexRange ('" + Range + "', " + Inclusive +") =");
        funRegexRange (Range, Inclusive, Debug);
        console.log ("");
    }
}

var Regex = funBuildRegexForRange( LowerRange, UpperRange, Debug ) 

for (i = 1000; i < 1020; i++) {
    TestNumber = i + ""
    if ( TestNumber.match(Regex)) {
        console.log(TestNumber + " TestNumber='" + TestNumber + "' matches");
    } else {
//      console.log(TestNumber + " does not match '" + Regex + "'")
    }
}
查看更多
Root(大扎)
3楼-- · 2020-06-27 09:54

Validating a number is in a range of numbers with regex is tricky problem. These regexs will match a number within a given range:

\b[0-9]{1,7}\b        #   0-9999999
\b[1-9][0-9]{2,6}\b   # 100-9999999
\b([4-9][0-9]{4}|[1-9][0-9]{5,6})\b   # 40000-9999999

It starts to get out of hand when you have complex ranges

\b(?:5(?:4(?:3(?:2[1-9]|[3-9][0-9])|[4-9][0-9]{2})|[5-9][0-9]{3})|[6-9][0-9]{4}|[1-9][0-9]{5}|[1-8][0-9]{6}|9(?:[0-7][0-9]{5}|8(?:[0-6][0-9]{4}|7(?:[0-5][0-9]{3}|6(?:[0-4][0-9]{2}|5(?:[0-3][0-9]|4[0-3]))))))\b # 54321-9876543

enter image description here

查看更多
登录 后发表回答