Using regular expressions to validate a numeric range

后端 未结 11 1724
自闭症患者
自闭症患者 2020-11-21 22:16

My input number is an int. But the input number must be in a range from -2055 to 2055 and I want to check this by using regular expression.

So is there anyway to wri

11条回答
  •  滥情空心
    2020-11-21 22:39

    As an alternate approach to the great expression offered by aliteralmind, here is one that is much longer but interesting in order to see what another approach might look like (and what not to do).

    It's an interesting exercise, because you can think of two distinct method: roughly, you can either:

    1. proceed by matching 4-char numbers, then 3-char numbers, etc.
    2. or proceed by matching the thousands digit, then the hundreds digit, etc.

    Without trying, how would you know which is best? It turns out that the first approach (aliteralmind's answer) is far more economical.

    Lower, I include a series of tests in the PHP language in case you or someone else would like to check the output.

    Below, I will give you the regex in "free-spacing mode", which allows comments inside the regex so you can easily understand what it does. However, not all regex engines support free-spacing mode, so before we start with the interesting part, here is the regex as a one-liner.

    Note that your question mentions numbers from -2055 to 2055. I assumed that you wanted to match "normal numbers", without leading zeroes. This means that the regex will match 999 but not 0999. If you would like leading zeroes, let me know, that is a very easy tweak.

    Also, if you are matching in utf-8 mode, the \d should be replaced by [0-9]. This is the more common form.

    The Regex as a One-Liner

    ^(?!-0$)-?(?:(?=\d{4}$)[12])?(?:(?=\d{3}$)(?:(?:(?<=^)|(?<=^-)|(?<=1)|(?<=-1))\d|(?:(?<=2)|(?<=-2))0))?(?:(?=\d{2}$)(?:(?:(?<=^)|(?<=^-)|(?<=^\d)|(?<=^-\d)|(?<=^\d{2})(?

    The Regex in Free-Spacing Mode

    (?x)                # free-spacing mode
    ^                   # anchor at beginning of string.
    (?!-0$)-?           # optional minus sign, but not for -0
    
    (?:                 # OPTIONAL THOUSANDS DIGIT
    (?=\d{4}$)[12]      # assert that the number is 4-digit long, match 1 or 2
    )?                  # end optional thousands digit
    
    
    (?:                 # OPTIONAL HUNDREDS DIGIT
    (?=\d{3}$)          # assert that there are three digits left
    (?:                 # non-capturing group
      (?:(?<=^)|(?<=^-)|(?<=1)|(?<=-1))\d    # if preceding chars are 1, -1 or head of string: value can be any digit
      |                      # or
      (?:(?<=2)|(?<=-2))0                # if preceding chars are 2 or -2: value must be 0
    )                   # close non-capturing group
    )?                  # end optional hundreds digits
    
    (?:             # OPTIONAL TENS DIGIT
    (?=\d{2}$)      # assert that there are two digits left
    (?:             # start non-capturing group
    
                    # if preceding char is head of string, single digit,
                    # or two digits that are not 20
                    # (with or without a minus)
                    # value can be any digit
      (?:(?<=^)|(?<=^-)|(?<=^\d)|(?<=^-\d)|
         (?<=^\d{2})(?

    Series of Tests

    These tests try to match against numbers from -100000 to 100000. They produce the following output:

    Successful test: matches from -2055 to 2055
    Successful test: NO matches from -100000 to -2056
    Successful test: NO matches from 2056 to 100000
    

    Here is the code:

    ";
        }
    }
    echo $success."
    "; // Test 2 $success="Successful test: NO matches from -100000 to -2056"; for($i=-100000;$i<=-2056;$i++) { $chari = sprintf("%d",$i); if (preg_match($regex,$chari)) { $success="Failed test: NO matches from -100000 to -2056"; echo $chari.": Match!
    "; } } echo $success."
    "; // Test 3 $success="Successful test: NO matches from 2056 to 100000"; for($i=2056;$i<=100000;$i++) { $chari = sprintf("%d",$i); if (preg_match($regex,$chari)) { $success="Failed test: NO matches from 2056 to 100000"; echo $chari.": Match!
    "; } } echo $success."
    "; ?>

    Speed Tests

    Here is the output of my simple speed test, matching from -1M to +1M. As Casimir pointed out, if aliteralmind's expression were anchored, instead of being slower it would be faster by 25%!

    zx81: 3.796217918396
    aliteralmind: 3.9922280311584
    difference: 5.1632998151294 percent longer
    

    Here is the test code:

    $regex="~(?x)^(?!-0$)-?(?:(?=\d{4}$)[12])?(?:(?=\d{3}$)(?:(?:(?<=^)|(?<=^-)|(?<=1)|(?<=-1))\d|(?:(?<=2)|(?<=-2))0))?(?:(?=\d{2}$)(?:(?:(?<=^)|(?<=^-)|(?<=^\d)|(?<=^-\d)|(?<=^\d{2})(?";
    echo "aliteralmind: ".$alit."
    "; echo "difference: ".$diff." percent longer
    ";

提交回复
热议问题