构造正则表达式以匹配数字范围

Constructing Regular Expressions to match numeric ranges

本文关键字:数字 范围 正则表达式      更新时间:2023-09-26

我正在寻找一种方法来构建正则表达式来匹配由给定整数范围指定的数字输入,即:如果我传入的范围是1,3-4,那么返回的正则表达式将只匹配1,3和4。

我写了下面的方法来尝试这样做:

function generateRegex(values) {
    if (values == "*") {
        return new RegExp("^[0-9]+$");
    } else {
        return new RegExp("^[" + values + "]+$");
    }
}

我有问题,然而,有时我需要匹配两位数,如"8-16",我还需要确保,如果我传递一个个位数的值,如"1",生成的正则表达式只匹配1,而不是说11

我真的希望这仍然是一个相当小的代码片段,但我不确定足够的正则表达式知道如何做到这一点。非常感谢任何帮助!

编辑:我意识到我原来的段落不清楚,所以我编辑了它。我意识到我最初生成的正则表达式根本不起作用

regex不知道任何数字,只知道数字。所以[8-16]是无效的,因为你说的是8和1之间的匹配(而不是1和8)加上数字6。如果你想匹配数字,你必须从词法上考虑它们。例如,要匹配1到30之间的数字,您必须这样写(存在其他正则表达式):

/^(30|[1-2]'d|[1-9])$/

我确信这是4-8个小时:-)最后(在它无用的时候),这是一个很好的组合regex的练习。你可以自由尝试。如果我们不使用continueArray构造函数,它完全是jsLint的。

var BuildRegex = function(matches) {
    "use strict";
    var splits = matches.split(','),
        res = '^(',
        i, subSplit, min, max, temp, tempMin;
    if (splits.length === 0) {
        return new RegExp('^()$');
    }
    for (i = 0; i < splits.length; i += 1) {
        if (splits[i] === '*') {
            return new RegExp('^([0-9]+)$');
        }
        subSplit = splits[i].split('-');
        min = BuildRegex.Trim(subSplit[0], '0');
        if (min === '') {
            return null;
        }
        if (subSplit.length === 1) {
            res += min;
            res += '|';
            continue;
        } else if (subSplit.length > 2) {
            return null;
        }
        max = BuildRegex.Trim(subSplit[1], '0');
        if (max === '') {
            return null;
        }
        if (min.length > max.length) {
            return null;
        }
        // For 2-998 we first produce 2-9, then 10-99
        temp = BuildRegex.DifferentLength(res, min, max);
        tempMin = temp.min;
        if (tempMin === null) {
            return null;
        }
        res = temp.res;
        // Then here 100-998
        res = BuildRegex.SameLength(res, tempMin, max);
    }
    res = res.substr(0, res.length - 1);
    res += ')$';
    return new RegExp(res);
};
BuildRegex.Repeat = function(ch, n) {
    "use strict";
    return new Array(n + 1).join(ch);
};
BuildRegex.Trim = function(str, ch) {
    "use strict";
    var i = 0;
    while (i < str.length && str[i] === ch) {
        i += 1;
    }
    return str.substr(i);
};
BuildRegex.IsOnlyDigit = function(str, start, digit) {
    "use strict";
    var i;
    for (i = start; i < str.length; i += 1) {
        if (str[i] !== digit) {
            return false;
        }
    }
    return true;
};
BuildRegex.RangeDigit = function(min, max) {
    "use strict";
    if (min === max) {
        return min;
    }
    return '[' + min + '-' + max + ']';
};
BuildRegex.DifferentLength = function(res, min, max) {
    "use strict";
    var tempMin = min,
        i, tempMax;
    for (i = min.length; i < max.length; i += 1) {
        tempMax = BuildRegex.Repeat('9', i);
        res = BuildRegex.SameLength(res, tempMin, tempMax);
        tempMin = '1' + BuildRegex.Repeat('0', i);
    }
    if (tempMin > tempMax) {
        return null;
    }
    return {
        min: tempMin,
        res: res
    };
};
BuildRegex.SameLength = function(res, min, max) {
    "use strict";
    var commonPart;
    // 100-100
    if (min === max) {
        res += min;
        res += '|';
        return res;
    }
    for (commonPart = 0; commonPart < min.length; commonPart += 1) {
        if (min[commonPart] !== max[commonPart]) {
            break;
        }
    }
    res = BuildRegex.RecursivelyAddRange(res, min.substr(0, commonPart), min.substr(commonPart), max.substr(commonPart));
    return res;
};
BuildRegex.RecursivelyAddRange = function(res, prefix, min, max) {
    "use strict";
    var only0Min, only9Max, i, middleMin, middleMax;
    if (min.length === 1) {
        res += prefix;
        res += BuildRegex.RangeDigit(min[0], max[0]);
        res += '|';
        return res;
    }
    // Check if 
    only0Min = BuildRegex.IsOnlyDigit(min, 1, '0');
    only9Max = BuildRegex.IsOnlyDigit(max, 1, '9');
    if (only0Min && only9Max) {
        res += prefix;
        res += BuildRegex.RangeDigit(min[0], max[0]);
        for (i = 1; i < min.length; i += 1) {
            res += '[0-9]';
        }
        res += '|';
        return res;
    }
    middleMin = min;
    if (!only0Min) {
        res = BuildRegex.RecursivelyAddRange(res, prefix + min[0], min.substr(1), BuildRegex.Repeat('9', min.length - 1));
        if (min[0] !== '9') {
            middleMin = String.fromCharCode(min.charCodeAt(0) + 1) + BuildRegex.Repeat('0', min.length - 1);
        } else {
            middleMin = null;
        }
    }
    middleMax = max;
    if (!only9Max) {
        if (max[0] !== '0') {
            middleMax = String.fromCharCode(max.charCodeAt(0) - 1) + BuildRegex.Repeat('9', max.length - 1);
        } else {
            middleMax = null;
        }
    }
    if (middleMin !== null && middleMax !== null && middleMin[0] <= middleMax[0]) {
        res = BuildRegex.RecursivelyAddRange(res, prefix + BuildRegex.RangeDigit(middleMin[0], middleMax[0]), middleMin.substr(1), middleMax.substr(1));
    }
    if (!only9Max) {
        res = BuildRegex.RecursivelyAddRange(res, prefix + max[0], BuildRegex.Repeat('0', max.length - 1), max.substr(1));
    }
    return res;
};
// ----------------------------------------------------------
var printRegex = function(p) {
    "use strict";
    document.write(p + ': ' + BuildRegex(p) + '<br>');
};
printRegex('*');
printRegex('1');
printRegex('1,*');
printRegex('1,2,3,4');
printRegex('1,11-88');
printRegex('1,11-88,90-101');
printRegex('1-11111');
printRegex('75-11119');

在此测试http://jsfiddle.net/dnqYV/

c#版本在这里http://ideone.com/3aEt3E

我不确定是否有一种(理智的)方法来测试RegExp的整数范围。我相信您关注的是RegExp,那里有更简单(更灵活)的方法。看一下IntRangeTest()。

var range = new IntRangeTest('0,10-20');
console.log(
    "0,10-20",
    range.test("") == false,
    range.test("-5") == false,
    range.test("0") == true,
    range.test("5") == false,
    range.test("11") == true,
    range.test("123.23") == false
);

如果你喜欢,你可以很容易地将它添加到Number.prototype中。您也可以很容易地将其作为RegExp的扩展,如果这是您所担心的。

好了,看来有四种情况我需要解决:

  • 个位数,即1,将简单地生成正则表达式/^1$/
  • 多个数字,如12,将需要正则表达式/^12&/
  • 个位数范围,即3-6,将生成正则表达式/^[3-6]$/
  • 最后,多位数范围以与多位数相似的方法工作,但具有范围,即11-14将成为/^1[1-4]$/。如果它们跨越多个起始数字,则需要拆分为多个正则表达式,例如23-31将变成/^2[3-9]|3[0-1]$/

因此,我所需要做的就是识别这些情况,并像xanatos建议的那样使用|创建一个复合正则表达式。例如,要匹配上述所有标准,将生成如下的正则表达式:

/^( 1 | 12 | [3-6] | 1[1-4] | 2[3-9]|3[0-1] )$/

其他人是否同意这似乎是一个体面的进展方式?