【问题标题】:How to match numbers between X and Y with regexp?如何使用正则表达式匹配 X 和 Y 之间的数字?
【发布时间】:2018-04-06 23:37:01
【问题描述】:

我想用 RegExp 匹配一个 X 和 Y 之间的数字。可以吗?

([0-9]+) 将匹配任何数字,我该如何匹配介于 110 和 2234 之间的数字?

【问题讨论】:

  • 是的,这是可能的。用正在运行的电锯剪脚趾甲也是如此。两者都不是很好的想法。

标签: regex


【解决方案1】:

由于在线数字范围正则表达式生成器服务经常在一段时间后变得不可用(this one 在撰写本文时仍然存在),我认为这里有它会很好。

请确保您在文本输入字段中输入最小和最大阈值,在下面标记您需要的所有选项并点击生成

!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).toRegexRange=e()}}(function(){return function(){return function e(t,n,r){function i(u,a){if(!n[u]){if(!t[u]){var s="function"==typeof require&&require;if(!a&&s)return s(u,!0);if(o)return o(u,!0);var c=new Error("Cannot find module '"+u+"'");throw c.code="MODULE_NOT_FOUND",c}var f=n[u]={exports:{}};t[u][0].call(f.exports,function(e){return i(t[u][1][e]||e)},f,f.exports,e,t,n,r)}return n[u].exports}for(var o="function"==typeof require&&require,u=0;u<r.length;u++)i(r[u]);return i}}()({1:[function(e,t,n){"use strict";const r=e("is-number"),i=(e,t,n)=>{if(!1===r(e))throw new TypeError("toRegexRange: expected the first argument to be a number");if(void 0===t||e===t)return String(e);if(!1===r(t))throw new TypeError("toRegexRange: expected the second argument to be a number.");let o={relaxZeros:!0,...n};"boolean"==typeof o.strictZeros&&(o.relaxZeros=!1===o.strictZeros);let s=e+":"+t+"="+String(o.relaxZeros)+String(o.shorthand)+String(o.capture)+String(o.wrap);if(i.cache.hasOwnProperty(s))return i.cache[s].result;let c=Math.min(e,t),f=Math.max(e,t);if(1===Math.abs(c-f)){let n=e+"|"+t;return o.capture?`(${n})`:!1===o.wrap?n:`(?:${n})`}let l=h(e)||h(t),d={min:e,max:t,a:c,b:f},p=[],g=[];if(l&&(d.isPadded=l,d.maxLen=String(d.max).length),c<0){g=u(f<0?Math.abs(f):1,Math.abs(c),d,o),c=d.a=0}return f>=0&&(p=u(c,f,d,o)),d.negatives=g,d.positives=p,d.result=function(e,t,n){let r=a(e,t,"-",!1,n)||[],i=a(t,e,"",!1,n)||[],o=a(e,t,"-?",!0,n)||[];return r.concat(o).concat(i).join("|")}(g,p,o),!0===o.capture?d.result=`(${d.result})`:!1!==o.wrap&&p.length+g.length>1&&(d.result=`(?:${d.result})`),i.cache[s]=d,d.result};function o(e,t,n){if(e===t)return{pattern:e,count:[],digits:0};let r=function(e,t){let n=[];for(let r=0;r<e.length;r++)n.push([e[r],t[r]]);return n}(e,t),i=r.length,o="",u=0;for(let e=0;e<i;e++){let[t,i]=r[e];t===i?o+=t:"0"!==t||"9"!==i?o+=p(t,i,n):u++}return u&&(o+=!0===n.shorthand?"\\d":"[0-9]"),{pattern:o,count:[u],digits:i}}function u(e,t,n,r){let i,u=function(e,t){let n=1,r=1,i=f(e,n),o=new Set([t]);for(;e<=i&&i<=t;)o.add(i),i=f(e,n+=1);for(i=l(t+1,r)-1;e<i&&i<=t;)o.add(i),i=l(t+1,r+=1)-1;return(o=[...o]).sort(s),o}(e,t),a=[],c=e;for(let e=0;e<u.length;e++){let t=u[e],s=o(String(c),String(t),r),f="";n.isPadded||!i||i.pattern!==s.pattern?(n.isPadded&&(f=g(t,n,r)),s.string=f+s.pattern+d(s.count),a.push(s),c=t+1,i=s):(i.count.length>1&&i.count.pop(),i.count.push(s.count[0]),i.string=i.pattern+d(i.count),c=t+1)}return a}function a(e,t,n,r,i){let o=[];for(let i of e){let{string:e}=i;r||c(t,"string",e)||o.push(n+e),r&&c(t,"string",e)&&o.push(n+e)}return o}function s(e,t){return e>t?1:t>e?-1:0}function c(e,t,n){return e.some(e=>e[t]===n)}function f(e,t){return Number(String(e).slice(0,-t)+"9".repeat(t))}function l(e,t){return e-e%Math.pow(10,t)}function d(e){let[t=0,n=""]=e;return n||t>1?`{${t+(n?","+n:"")}}`:""}function p(e,t,n){return`[${e}${t-e==1?"":"-"}${t}]`}function h(e){return/^-?(0+)\d/.test(e)}function g(e,t,n){if(!t.isPadded)return e;let r=Math.abs(t.maxLen-String(e).length),i=!1!==n.relaxZeros;switch(r){case 0:return"";case 1:return i?"0?":"0";case 2:return i?"0{0,2}":"00";default:return i?`0{0,${r}}`:`0{${r}}`}}i.cache={},i.clearCache=(()=>i.cache={}),t.exports=i},{"is-number":2}],2:[function(e,t,n){"use strict";t.exports=function(e){return"number"==typeof e?e-e==0:"string"==typeof e&&""!==e.trim()&&(Number.isFinite?Number.isFinite(+e):isFinite(+e))}},{}]},{},[1])(1)});


$( document ).ready( function() {
  $( "#rangeLeft, #rangeRight" ).keydown( function() {
    clearDisplay();
  } );
  $('#wholestring').click(function() {
        $('#wholestring').attr('checked', 'checked');
        $('#wb').attr('checked', false);
        $('#dgtb').attr('checked', false);
        $('#whtb').attr('checked', false);
    })
  $('#wb').click(function() {
        $('#wb').attr('checked', 'checked');
        $('#wholestring').attr('checked', false);
        $('#dgtb').attr('checked', false);
        $('#whtb').attr('checked', false);
    })
  $('#dgtb').click(function() {
        $('#dgtb').attr('checked', 'checked');
        $('#wb').attr('checked', false);
        $('#wholestring').attr('checked', false);
        $('#whtb').attr('checked', false);
    })
  $('#whtb').click(function() {
        $('#whtb').attr('checked', 'checked');
        $('#wb').attr('checked', false);
        $('#dgtb').attr('checked', false);
        $('#wholestring').attr('checked', false);
    })
  $( "#run" ).click( function() {
    clearDisplay();
    var rangeLeft = $( "#rangeLeft" ).val();
    var rangeRight = $( "#rangeRight" ).val();
    if ( ! checkRanges( rangeLeft,  rangeRight ) ) return;
    let source = toRegexRange(rangeLeft, rangeRight);
    
    if ($('#frac').is(':checked')) {
        source = source + '(?:\\.\\d+)?';
    }
    if ($('#allowzero').is(':checked')) {
        source = "0*" + source;
    }
    if ($('#neg').is(':checked')) {
        source = "-?" + source;
    }
    if ( $('#wholestring').is(':checked')) {
        source = '^' + source + '$';
    } else if ( $('#wb').is(':checked')) {
        source = '\\b' + source + '\\b';
    } else if ( $('#whtb').is(':checked')) {
        source = '(?<!\\S)' + source + '(?!\\S)';
    } else if ( $('#dgtb').is(':checked')) {
        source = '(?<!\\d)' + source + '(?!\\d)';
    }
    $( "#result" ).append( "<B>" + source.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;') + "</B><BR/>" );
  } );
} );

function checkRanges( rangeLeft, rangeRight ) {
  if ( /\D/.test( rangeLeft ) || /\D/.test( rangeRight ) ) {
    $( "#result" ).append( "Type two numbers<BR/>" );
    return false;
  }
  rangeLeft = parseInt( rangeLeft );
  rangeRight = parseInt( rangeRight );
  if ( isNaN( rangeLeft ) || isNaN( rangeRight ) ) $( "#result" ).append( "Range boundaries are not specified<BR/>" );
  if ( rangeLeft < 0 ) $( "#result" ).append( "Left boundary is less than 0<BR/>" );
  if ( rangeRight < 0 ) $( "#result" ).append( "Right boundary is less than 0<BR/>" );
  if ( rangeLeft > rangeRight ) $( "#result" ).append( "Left boundary is greater than the right boundary<BR/>" );
  return( !(
    rangeLeft < 0 ||
    rangeRight < 0 ||
    rangeLeft > rangeRight ||
    isNaN( rangeLeft ) ||
    isNaN( rangeRight )
  ) );
}

function clearDisplay() {
  $( "#result" ).html( "" );
  $( "#test" ).hide();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <p>Type in minimum and maximum values and click <i>Generate</i>.</p>
    <INPUT id="rangeLeft" value=1 /> - <INPUT id="rangeRight" value=365 />
    <BR/>
    <BUTTON id="run">Generate</BUTTON>
    <pre><code id="result" /></pre>
    <div>
       <label><input type="checkbox" id="wholestring"/>Match whole string</label> <br/>
       <label><input type="checkbox" id="wb"/>Match within word boundaries</label><br/>
       <label><input type="checkbox" id="dgtb"/>Match when not enclosed with digits</label><br/>
       <label><input type="checkbox" id="whtb"/>Match when not enclosed with whitespaces</label><br/>
       <label><input type="checkbox" id="allowzero"/>Allow leading zeros</label><br/>
       <label><input type="checkbox" id="neg"/>Optionally match negative numbers</label><br/>
       <label><input type="checkbox" id="frac"/>Optionally match fractional digits (floats)</label><br/>
    </div>

这里的大部分 JavaScript 代码都是从 Алгоритм для преобразования диапазона номеров в регулярное выражениеto-regex-range npm 库中借来的。

【讨论】:

    【解决方案2】:

    另外,如果您想定位或查找您的路径名是否包含年份,并将其作为字符串取出,您可以尝试以下操作:

    path1 = r'X:\S\Something_2019\y2019\AB19778_description\subfolder1\subfolder2'
    find = re.findall(r'.*(y[1-2][0,9][0-9]{2})', path1)
    mystring = find[0]
    print(mystring)
    

    如果存在格式为“yYYYY”的年份字符串,则检查“path1”。所以用字母'y'作为前缀(与我的研究案例相关)。 这将返回字符串 'y2019'。

    【讨论】:

      【解决方案3】:

      根据Generate a Regular Expression to Match an Arbitrary Numeric Range,在为Regex_For_Range 的示例生成这样的正则表达式之后:

      \b0*(1[1-9][0-9]|[2-9][0-9]{2}|1[0-9]{3}|2[01][0-9]{2}|22[0-2][0-9]|223[0-4])\b
      

      会成功的。

      这个过程将是(仍然遵循那个正则表达式生成器):

      首先,分成等长的范围:

      110 - 999
      1000 - 2234
      

      其次,分解产生简单正则表达式的范围:

      110 - 199
      200 - 999
      1000 - 1999
      2000 - 2199
      2200 - 2229
      2230 - 2234
      

      将每个范围变成一个正则表达式:

      1[1-9][0-9]
      [2-9][0-9]{2}
      1[0-9]{3}
      2[01][0-9]{2}
      22[0-2][0-9]
      223[0-4]
      

      折叠相邻的 10 次方: 1[1-9][0-9] [2-9][0-9]{2} 1[0-9]{3} 2[01][0-9]{2} 22[0-2][0-9] 223[0-4]

      结合上面的正则表达式产生:

      0*(1[1-9][0-9]|[2-9][0-9]{2}|1[0-9]{3}|2[01][0-9]{2}|22[0-2][0-9]|223[0-4])
      

      接下来我们将尝试使用树分解常见前缀:
      根据正则表达式前缀解析成树:

      . 1 [1-9] [0-9]
      + [0-9]{3}
      + [2-9] [0-9]{2}
      + 2 [01] [0-9]{2}
      + 2 [0-2] [0-9]
      + 3 [0-4]
      

      将解析树转换为正则表达式:

      0*(1([1-9][0-9]|[0-9]{3})|[2-9][0-9]{2}|2([01][0-9]{2}|2([0-2][0-9]|3[0-4])))
      

      我们选择较短的作为我们的结果。

      \b0*(1[1-9][0-9]|[2-9][0-9]{2}|1[0-9]{3}|2[01][0-9]{2}|22[0-2][0-9]|223[0-4])\b
      

      【讨论】:

      • 一门 C#/VB.NET/PHP 或任何其他语言,除了我不知道的那种会很棒。 ;)
      • 上面没有及时编辑,我的意思是用前导0填充的有效数字:0110、00110、000110...
      • 链接请求凭证供查看
      • @Doberon 是的,谢谢。我已经恢复了链接,但我认为在线脚本仍然无法正常工作。
      【解决方案4】:

      您可以将以下范围的正则表达式放在一起:

      1[1-9]\d  = 110-199
      [2-9]\d\d = 200-999
      1\d\d\d   = 1000-1999
      2[0-1]\d\d= 2000-2199
      22[0-2]\d = 2200-2229
      223[0-4]  = 2230-2234
      

      形成:

      (1[1-9]\d|[2-9]\d\d|1\d\d\d|2[0-1]\d\d|22[0-2]\d|223[0-4])
      

      \d 表示 [0-9],但少了三个字符

      【讨论】:

        【解决方案5】:

        虽然您可以使用一些看起来很荒谬的正则表达式(正如 VonC 回答的那样),但正则表达式真的不应该这样做。为什么不将号码检查推迟到重定向到脚本?

        如果数字 110-2234 转到 script1,而 1-109 转到 script2,则将所有数字指向 router 脚本并将其重定向到正确的位置(通过 HTTP 重定向)..

        .htaccess:

        RewriteRule ^view/([0-9]+)/?$ router.php?page=$1 [L]
        

        ..然后在router.php,类似:

        <?PHP
        if(
           int($_GET['page']) > 110 &&
           int($_GET['page']) < 2234
        ){
            header("Status: 301 Moved Permanently\nLocation: /script1");
        }else{
            header("Status: 404 Not Found");
        }
        ?>
        

        【讨论】:

        • 我知道。我想在 .htaccess 中制作它并避免加载 php 而只是加载正确的静态文件:-)
        【解决方案6】:

        有可能虽然不漂亮。

        \b(?:[1][1][0-9]|1\d{3}|223[0-4]|2[0-1]\d\d|2[0-2][0-3][0-4])\b
        

        我在 2006 年向 PCRE 的作者 Phillip Hazel 发送了电子邮件,他对正则表达式中的数学的看法:

        在您看来,这可能超出了项目的范围:将数字视为数字而不是文本的能力,这绝对是一个值得的功能。 允许您对匹配的数字进行一些基本的数学检查,例如:第二个匹配的数字是更高还是更低,第三个数字是第一个数字的倍数,以及许多更复杂的情况,我不会详细说明只是为了让我的观点跨越. 你觉得这超出了文本匹配的范围吗?

        我收到以下回复:

        是的,我想我知道,而且,它不是 Perl 中可用的东西 常用表达。我知道PCRE 确实有一些来自 Perl 的扩展, 但没有什么比这更重要的了(你 也许可以使用 标注,但这有点广告 hoc,而且毫无疑问非常混乱!)。

        菲利普

        在 09 年我完全同意。只需匹配所有数字并使用您正在匹配的任何语言进行数字验证。

        【讨论】:

        • 我不愿意修正这句话的拼写,但我不希望错误的拼写模因传播开来。
        • 这与 120-199 范围内的数字不匹配
        【解决方案7】:

        这不是正则表达式擅长的事情。您可能会发现更容易确保您拥有正确的位数 /^([0-9]{3,4})$/,然后针对捕获进行进一步检查。

        【讨论】:

          猜你喜欢
          • 2023-02-08
          相关资源
          最近更新 更多