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

时间:2011-10-25 09:20:36

标签: javascript regex

我正在寻找一种方法来构造正则表达式来匹配给定整数范围指定的数字输入,即。如果我传入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

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

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

4 个答案:

答案 0 :(得分:1)

正则表达式对数字一无所知,只有数字。因此,[8-16]无效,因为您说8和1之间的匹配(而不是1和8,例如)加上数字6。 如果你想匹配数字,你必须从词汇上考虑它们。例如,要匹配1到30之间的数字,您必须编写类似(存在其他正则表达式)的内容:

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

答案 1 :(得分:1)

我确信它是4-8小时:-)最后(并且无用)这是一个很好的练习组成正则表达式。你可以自由尝试。如果我们排除continue的一次使用并使用Array构造函数,则完全可以使用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

答案 2 :(得分:0)

好的,似乎我需要解决4个主要案例:

  • 单个数字,即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] )$/

其他人是否认为这似乎是一种体面的进步方式?

答案 3 :(得分:0)

我不确定是否有一种(理智的)方法来使用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的扩展。