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

问题描述 投票:-2回答:1

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

  • 我需要使用正则表达式
  • 正则表达式需要根据给定的数字(最小和最大)生成
  • 需要只检查浮点数
  • 数字可能是积极的也可能是消极的
  • 可能的浮动格式:+ 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
regex algorithm
1个回答
2
投票

正则表达式不适合测试某个数字是否在某个范围内;他们可以长得很久。

下面是一个用runnable 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>
© www.soinside.com 2019 - 2024. All rights reserved.