将整数数字间隔转换为正则表达式

时间:2013-08-22 07:30:02

标签: regex algorithm

SO,

我正在寻找有关该问题的解决方案 - 如何将整数间隔转换为正则表达式。假设我有两个数字AB。它们都是正整数,让A < B

现在,我正在寻找能够产生单个正则表达式的算法(可能是代码),它将匹配AB之间的数字(包括边框)。例如,我有A=20B=35,然后正确的正则表达式是^2[0-9]$|^3[0-5]$ - 因为只有数字20..35才适合它。

通常情况下,当A类似于83724而B类似于28543485时,它就不那么明显了。

UPD。大多数情况下,这是好奇心的问题。我知道最好的方法:返回结果:A<=X && X<=B

3 个答案:

答案 0 :(得分:4)

为什么在这种情况下使用正则表达式?

我会这样做:

boolean isBetween = num > A && num < B;

(用Java编写的代码)

更容易,像你要求的正则表达式可能是巨大的,在这种情况下使用它将是毫无意义和低效的。

祝你好运。

如果你真的坚持使用RegEx执行此任务,请参阅this website,运行带有详细模式的正则表达式,它将向您解释作者的RegEx如何工作。

答案 1 :(得分:2)

正如其他人已经告诉过你的那样,这不是一个好主意。它不会比仅匹配所有整数更快,并在之后过滤它们。但无论如何我会回答你的问题。

根据间隔的大小,您可以让正则表达式引擎为您优化它,因此您只需输出| - 分隔的值列表。这可以通过有限自动机理论的基本算法在算法上最小化。

对于大间隔,这可能是内存密集型的。在这种情况下,您可以一次匹配A和B中所有不同长度的数字。在您的示例中,所有6-7位数字都可以轻松与[0-9][1-9]{5,6}匹配。现在你还剩下了边框情况,你可以通过递归方式创建(对于A面,在这种情况下,我没有包括递归的基本情况):

  1. 让S成为A.
  2. 设f为S的第一位数,g=f+1,n为(digits of S)-1
  3. 向正则表达式添加一个段,其中包含大于f的数字:[g-9][0-9]{n}
  4. 为以f:f(recursive call starting from step 2, with S=the rest of digits of S)
  5. 开头的数字添加细分

    因此,对于A=123,我们最终会得到类似的内容(仅为“可读性”添加空格):

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

答案 2 :(得分:2)

我已经完成了这个(在PHP中):

class Converter
{
    const REGEXP_OR     = '|';
    const REGEXP_START  = '^';
    const REGEXP_END    = '$';

    protected $sStart;
    protected $sEnd;
    function __construct($mStart, $mEnd=null)
    {
        if(is_array($mStart) && count($mStart)>1)
        {
            $this->sStart = (string)($mStart[0]);
            $this->sEnd   = (string)($mStart[1]);
        }
        else
        {
            $this->sStart = (string)($mStart);
            $this->sEnd   = (string)($mEnd);
        }
        if((int)($mStart)>(int)($mEnd))
        {
            $this->sStart = $this->sEnd = null;
        }
    }

    public function getRegexp()
    {
        return self::REGEXP_START.$this->_get_regexp_by_range($this->sStart, $this->sEnd).self::REGEXP_END;
    }

    protected function _get_regexp_by_range($sStart, $sEnd, $sOr=self::REGEXP_OR, $sFrom=self::REGEXP_START, $sTill=self::REGEXP_END)
    {
       if(!isset($sStart) || !isset($sEnd))
       {
           return null;
       }
       if((int)($sStart)>(int)($sEnd))
       {
          return null;
       }
       elseif($sStart==$sEnd)
       {
          return $sStart;
       }
       elseif(strlen($sEnd)>strlen($sStart))
       {
          $rgRegexp  = array($this->_get_regexp_by_range($sStart, str_repeat('9', strlen($sStart))));
          for($i=strlen($sStart)+1; $i<strlen($sEnd)-1; $i++)
          {
             $rgRegexp[] = $this->_get_regexp_by_range('1'.str_repeat('0', $i), str_repeat('9', $i+1));
          }
          $rgRegexp[] = $this->_get_regexp_by_range('1'.str_repeat('0', strlen($sEnd)-1), $sEnd);
          return join($sTill.$sOr.$sFrom, $rgRegexp);
       }
       else
       {
          $rgRegexp   = array();
          for($iIntersect=0;$iIntersect<strlen($sStart);$iIntersect++)
          {
             if($sStart[$iIntersect]!=$sEnd[$iIntersect])
             {
                break;
             }
          }
          if($iIntersect)
          {
             return join($sTill.$sOr.$sFrom, array_map(function($sItem) use ($iIntersect, $sStart)
             {
                return substr($sStart, 0, $iIntersect).$sItem;
             }, explode($sTill.$sOr.$sFrom, $this->_get_regexp_by_range(substr($sStart, $iIntersect), substr($sEnd, $iIntersect)))));
          }
          else
          {
             $rgRegexp = array($sStart);
             for($iPos=strlen($sStart)-1; $iPos>0; $iPos--)
             {
                if($sStart[$iPos]+1<10)
                {
                   $rgRegexp[]=substr($sStart, 0, $iPos).'['.($sStart[$iPos]+1).'-'.'9'.']'.str_repeat('[0-9]', strlen($sStart)-$iPos-1);
                }
             }
             if(($sStart[0]+1)<($sEnd[0]-1))
             {
                $rgRegexp[]='['.($sStart[0]+1).'-'.($sEnd[0]-1).']'.str_repeat('[0-9]', strlen($sStart)-1);
             }
             elseif((int)($sStart[0])+1==(int)($sEnd[0])-1)
             {
                $rgRegexp[]=($sStart[0]+1).str_repeat('[0-9]', strlen($sStart)-1);
             }
             for($iPos=1; $iPos<strlen($sEnd); $iPos++)
             {
                if($sEnd[$iPos]-1>=0)
                {
                  $rgRegexp[]=substr($sEnd,0, $iPos).'['.'0'.'-'.($sEnd[$iPos]-1).']'.str_repeat('[0-9]', strlen($sEnd)-$iPos-1);
                }
             }
             $rgRegexp[]=$sEnd;
             return join($sTill.$sOr.$sFrom, $rgRegexp);
          }
       }
    }
}

然后,它会得到任何字符串的正确结果,但我认为最终的正则表达式不是最好的。

$sPattern = (new Converter('1', '1000000000'))->getRegexp();
var_dump(
   preg_match('/'.$sPattern.'/', '10000000000'), 
   preg_match('/'.$sPattern.'/', '100000000'));

无论如何,非常感谢所有回答的人。