SO,
我正在寻找有关该问题的解决方案 - 如何将整数间隔转换为正则表达式。假设我有两个数字A
和B
。它们都是正整数,让A < B
现在,我正在寻找能够产生单个正则表达式的算法(可能是代码),它将匹配A
和B
之间的数字(包括边框)。例如,我有A=20
,B=35
,然后正确的正则表达式是^2[0-9]$|^3[0-5]$
- 因为只有数字20..35才适合它。
通常情况下,当A
类似于83724而B
类似于28543485时,它就不那么明显了。
UPD。大多数情况下,这是好奇心的问题。我知道最好的方法:返回结果:A<=X && X<=B
答案 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面,在这种情况下,我没有包括递归的基本情况):
g=f+1
,n为(digits of S)-1
[g-9][0-9]{n}
f(recursive call starting from step 2, with S=the rest of digits of S)
因此,对于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'));
无论如何,非常感谢所有回答的人。