生成正则表达式以检查数字是否在范围内的算法(任意范围)

时间:2019-03-07 07:47:11

标签: regex algorithm

我需要一种用于生成正则表达式的算法,该算法将检查数字是否在特定范围内。一般来说,我有以下要求:

  • 我需要使用正则表达式
  • 需要根据给定的数字(最小和最大)生成正则表达式
  • 只需要检查浮点数
  • 数字可能为正也可能为负
  • 可能的浮点格式:+ X.YYY,+ X.YY,+ X.Y,-X.YYY,-X.YY,-X.Y

    X代表任意数量的数字,而Y恰好代表一位数字。至少需要一位小数。所以1应该是1.0,0应该是0.0,依此类推。

    最小和最大将始终具有相同的格式。因此,您可以让min = + 2.22,max = +3.45并检查+1.541,但不能让min = + 2.223,max = +3.45并检查+1.541

下面您可以找到范围的示例:

  • 从+1.0到+20.0
  • 从-1.0到+20.0
  • 从-20.0到-10.0
  • 从+1.01到+2.12

我对正则表达式不是很熟悉,更确切地说,我什至不知道我应该从哪里开始。将不胜感激任何建议!

我的想法

我的想法是创建子范围。所以说我要检查范围7.5-222.1。然后,我相信我应该创建子范围并进行检查。例如:

  • 7.5-7.9
  • 8.0-9.9
  • 10.0-99.9
  • 100.0-222.0
  • 222.0-222.1

1 个答案:

答案 0 :(得分:2)

正则表达式并不真正适合测试数字是否在一定范围内;他们会变得很长。

下面是用可运行的JavaScript代码段编写的解决方案。您可以在输入框中输入范围和测试值,并同时显示生成的正则表达式和测试值的验证结果。

以下假设/规则适用:

  • 如果范围无效(例如,最小数字大于最大数字),则正则表达式将为_^,这将使所有测试值失败。
  • 测试值的位数必须与最小值/最大值相同。
  • 当最大值和最小值具有不同的十进制数字时,两者中较大的一个
  • +是不允许的;非负数不应带有符号
  • -0.0是不允许的;零不应该有一个符号
  • .9是不允许的;小数点前至少应有一位数字
  • 最小值/最大值的规则不太严格。
  • 最小值和最大值不必具有相同数目的整数。例如,从-33.08到12328.84的范围可以正常工作。

// Some helper constants/functions for producing regex
const reDot = "\\.";

function reRange(low, high) {
    return high-low === 9 ? "\\d" : low<high ? "[" + low + "-" + high + "]" : low;
}

function reRepeat(what, min, max=min) {
    return !max ? ""
        : what + (max > 1 ? "{" + min + (min < max ? "," + max : "") + "}" : min ? "" : "?");
}

function reOr(list) {
    return list.length > 1 ? "(" + list.join("|") + ")" : list[0];
}

function reAnchor(what) {
    return "^" + what + "$";
}

// Main function:
function rangeRegex(min, max) {
    if (!(+min <= +max)) return "_^"; // All strings should fail this regex
    const decimals = Math.max( (min+".").split(".")[1].length, (max+".").split(".")[1].length );
    // Take care of negative ranges:
    if (+min < 0 && +max < 0) return reAnchor("-" + positiveRange(-max, -min));
    if (+min < 0) return reAnchor(reOr(["-(?=.*[1-9])" + positiveRange(0, -min), positiveRange(0, max)]));
    return reAnchor(positiveRange(min, max));
    
    function positiveRange(min, max) {
        // Format the two input numbers with equal number of decimals and remove decimal point
        const minParts = (Math.abs(min)+".").split(".");
        const maxParts = (Math.abs(max)+".").split(".");
        min = minParts[0] + minParts[1].padEnd(decimals, "0"); 
        max = maxParts[0] + maxParts[1].padEnd(decimals, "0");
        // Build regex parts
        const parts = [];
        if (min.length < max.length && !/^1?0*$/.test(min)) {
            parts.push(fixedLengthRange(min, "9".repeat(min.length)));
            min = "1" + "0".repeat(min.length);
        }
        if (min.length < max.length && !/^9+$/.test(max)) {
            parts.push(fixedLengthRange("1" + "0".repeat(max.length-1), max));
            max = "9".repeat(max.length-1);
        }
        if (/^1?0*$/.test(min) && /^9+$/.test(max)) {
            parts.push(
                reRange(min[0], 9) 
                    + reRepeat(reRange(0, 9), min.length-decimals-1, max.length-decimals-1)
                    + (decimals ? reDot + reRepeat(reRange(0, 9), decimals) : "") 
            );
        } else {
            parts.push(fixedLengthRange(min, max));
        }
        return reOr(parts);
    }
    
    function fixedLengthRange(min, max) {
        const len = max.length;
        if (!len) return "";
        const pre = len === decimals ? reDot : "";
        let low = +min[0];
        let high = +max[0];
        if (low === high) return pre + min[0] + fixedLengthRange(min.slice(1), max.slice(1));
        const parts = [];
        if (+min.slice(1)) {
            parts.push(min[0] + fixedLengthRange(min.slice(1), "9".repeat(len-1)));
            low++;
        }
        if (max.slice(1) < "9".repeat(max.length-1)) {
            parts.push(max[0] + fixedLengthRange("0".repeat(len-1), max.slice(1)));
            high--;
        }
        if (low <= high) {
            parts.push(reRange(low, high) + 
                (len <= decimals || !decimals ? reRepeat(reRange(0, 9), len-1)
                : reRepeat(reRange(0, 9), len-1-decimals) + reDot + reRepeat(reRange(0, 9), decimals)));
        }
        return pre + reOr(parts);
    }
}

// I/O handling for this snippet

const inputMin = document.querySelector("#min");
const inputMax = document.querySelector("#max");
const inputVal = document.querySelector("#val");
const outputRegex = document.querySelector("#regex");
const outputValid = document.querySelector("#valid");

document.oninput = function() {
    const regex = rangeRegex(inputMin.value, inputMax.value);
    outputRegex.textContent = regex;
    outputValid.textContent = new RegExp(regex).test(inputVal.value) ? "OK" : "Not OK";
}
<label>Min: <input id="min"></label>
<label>Max: <input id="max"></label>
<hr>
<div>Regex: <span id="regex"></span></div>
<label>Test: <input id="val"></label>
<div>Valid: <span id="valid"></span></div>