数字范围的正则表达式生成器

时间:2015-11-04 01:09:15

标签: regex algorithm

我检查了stackExchange描述,算法问题是允许的主题之一。所以这里。

给定一个范围的输入,其中开始和结束的数字具有相同的位数(例如,2,3或4),我想编写代码来生成一组正则表达式,当针对a检查时反过来,告诉我这个数字是否在原始范围内。

例如:如果范围是145-387,则146,200和280将全部匹配生成的正则表达式之一,144,390(过去就是290)和445(过去说345)不会。

我一直在想结果将是一个正则表达式列表,如:

14[5-9]             // match 145-149
1[5-9]0-9]          // 150-199
2[0-9][0-9]         // 200-299
3[0-7][0-9]         // 300-379
38[0-7]             // 380-387

然后检查该号码的软件将测试以查看正在测试的3位数代码是否与这些代码匹配。

那么生成表达式的最佳方式是什么?

我提出的最新(系列)是:

  1. 确定两个范围编号不同的第一个数字(1145-1158,第一个不同的数字是第3个)
  2. 对于不同的数字,确定它们的第一个数字是否相差多于一个 - 如果是这样,那么它的范围得到它自己的正则表达式(在我们的例子中为200-299)
  3. 获得更低的范围:对于每个其他数字:从范围开头的第一个数字前缀,将数字增加1,将0s填充到相同的长度,并与具有9的数字配对在数字位置和所有填充的地方。在我们的示例中,增加4到5,填充到150,生成正则表达式以处理150-199。
  4. 获得更高的范围:对于每个其他数字:从范围结束开始的第一个数字的前缀,逐个减少的数字,0s的填充休息,在所有填充的0个位置与数字9s配对并且递减数字。在我们的例子中,正则表达式处理300-379。
  5. 我错过了什么吗?即使在上面我还有一些细节,我可以通过一个算法剑来扼杀细节。但我提出的其他事情甚至比这更糟糕。

9 个答案:

答案 0 :(得分:11)

这是我的解决方案和复杂度为O(log n)的算法(n是范围的结尾)。我相信这是最简单的一个:

基本上,将您的任务分成以下步骤:

  1. 渐渐地"弱化"范围的start
  2. 渐渐地"弱化"范围的end
  3. 合并这两个。
  4. 通过"弱化",我的意思是找到可以用这个特定数字的简单正则表达式表示的范围的结尾,例如:

    145 -> 149,150 -> 199,200 -> 999,1000 -> etc.
    

    这是一个向后的,对于范围的end

    387 -> 380,379 -> 300,299 -> 0
    

    合并将是注意299-> 0和200-> 999的重叠并将它们组合成200-> 299的过程。

    结果,你会得到这组数字(第一个列表完整,第二个反转:

    145, 149, 150, 199, 200, 299, 300, 379, 380, 387
    

    现在,这是有趣的部分。成对获取数字,并将它们转换为范围:

    145-149, 150-199, 200-299, 300-379, 380-387
    

    或者在正则表达式中:

    14[5-9], 1[5-9][0-9], 2[0-9][0-9], 3[0-7][0-9], 38[0-7]
    

    以下是weakening的代码如下所示:

    public static int next(int num) {
        //Convert to String for easier operations
        final char[] chars = String.valueOf(num).toCharArray();
        //Go through all digits backwards
        for (int i=chars.length-1; i>=0;i--) {
            //Skip the 0 changing it to 9. For example, for 190->199
            if (chars[i]=='0') {
                chars[i] = '9';
            } else { //If any other digit is encountered, change that to 9, for example, 195->199, or with both rules: 150->199
                chars[i] = '9';
                break;
            }
        }
    
        return Integer.parseInt(String.valueOf(chars));
    }
    
    //Same thing, but reversed. 387 -> 380, 379 -> 300, etc
    public static int prev(int num) {
        final char[] chars = String.valueOf(num).toCharArray();
        for (int i=chars.length-1; i>=0;i--) {
            if (chars[i] == '9') {
                chars[i] = '0';
            } else {
                chars[i] = '0';
                break;
            }
        }
    
        return Integer.parseInt(String.valueOf(chars));
    }
    

    其余的是技术细节,易于实施。以下是此O(log n)算法的实现:https://ideone.com/3SCvZf

    哦,顺便说一句,它也适用于其他范围,例如范围1-321654,结果是:

    [1-9]
    [1-9][0-9]
    [1-9][0-9][0-9]
    [1-9][0-9][0-9][0-9]
    [1-9][0-9][0-9][0-9][0-9]
    [1-2][0-9][0-9][0-9][0-9][0-9]
    3[0-1][0-9][0-9][0-9][0-9]
    320[0-9][0-9][0-9]
    321[0-5][0-9][0-9]
    3216[0-4][0-9]
    32165[0-4]
    

    对于129-131它来说:

    129
    13[0-1]
    

答案 1 :(得分:4)

我终于到了以下地方。总的想法是从范围的开头开始,产生一个正则表达式,该表达式将匹配到最多但不包括10的下一个倍数,然后是数百个等等,直到你匹配到最后的结果为止。范围;然后从范围的结尾开始向下工作,用0代替越来越多的数字以匹配相似的9的数字,以匹配特定的范围结束。然后为范围的一部分生成一个正则表达式,如果它们尚未覆盖它的全部。

我应该特别注意bezmax的例程,将两个数字转换为匹配它们的正则表达式 - 比直接处理字符串或字符数组要容易得多。

无论如何,这是:

package numbers;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

/**
 * Has methods for generating regular expressions to match ranges of numbers.
 */
public class RangeRegexGenerator
{
  public static void main(String[] args)
  {
    RangeRegexGenerator rrg = new RangeRegexGenerator();

//    do
//    {
//      Scanner scanner = new Scanner(System.in);
//      System.out.println("enter start, <return>, then end and <return>");
//      int start = scanner.nextInt();
//      int end = scanner.nextInt();
//      System.out.println(String.format("for %d-%d", start, end));

      List<String> regexes = rrg.getRegex("0015", "0213");
      for (String s: regexes) { System.out.println(s); }
//    } 
//    while(true);
  }

  /**
   * Return a list of regular expressions that match the numbers
   * that fall within the range of the given numbers, inclusive.
   * Assumes the given strings are numbers of the the same length,
   * and 0-left-pads the resulting expressions, if necessary, to the
   * same length. 
   * @param begStr
   * @param endStr
   * @return
   */
  public List<String> getRegex(String begStr, String endStr)
  {
      int start = Integer.parseInt(begStr);
      int end   = Integer.parseInt(endStr);
      int stringLength = begStr.length();
      List<Integer> pairs = getRegexPairs(start, end);
      List<String> regexes = toRegex(pairs, stringLength);
      return regexes;
  }

  /**
   * Return a list of regular expressions that match the numbers
   * that fall within the range of the given numbers, inclusive.
   * @param beg
   * @param end
   * @return
   */
  public List<String> getRegex(int beg, int end)
  {
    List<Integer> pairs = getRegexPairs(beg, end);
    List<String> regexes = toRegex(pairs);
    return regexes;
  }

  /**
   * return the list of integers that are the paired integers
   * used to generate the regular expressions for the given
   * range. Each pair of integers in the list -- 0,1, then 2,3,
   * etc., represents a range for which a single regular expression
   * is generated.
   * @param start
   * @param end
   * @return
   */
  private List<Integer> getRegexPairs(int start, int end)
  {
      List<Integer> pairs = new ArrayList<>();

      ArrayList<Integer> leftPairs = new ArrayList<>();
      int middleStartPoint = fillLeftPairs(leftPairs, start, end);
      ArrayList<Integer> rightPairs = new ArrayList<>();
      int middleEndPoint = fillRightPairs(rightPairs, middleStartPoint, end);

      pairs.addAll(leftPairs);
      if (middleEndPoint > middleStartPoint)
      {
        pairs.add(middleStartPoint);
        pairs.add(middleEndPoint);
      }
      pairs.addAll(rightPairs);
      return pairs;
  }

  /**
   * print the given list of integer pairs - used for debugging.
   * @param list
   */
  @SuppressWarnings("unused")
  private void printPairList(List<Integer> list)
  {
    if (list.size() > 0)
    {
      System.out.print(String.format("%d-%d", list.get(0), list.get(1)));
      int i = 2;
      while (i < list.size())
      {
        System.out.print(String.format(", %d-%d", list.get(i), list.get(i + 1)));
        i = i + 2;
      }
      System.out.println();
    }
  }

  /**
   * return the regular expressions that match the ranges in the given
   * list of integers. The list is in the form firstRangeStart, firstRangeEnd, 
   * secondRangeStart, secondRangeEnd, etc.
   * @param pairs
   * @return
   */
  private List<String> toRegex(List<Integer> pairs)
  {
    return toRegex(pairs, 0);
  }

  /**
   * return the regular expressions that match the ranges in the given
   * list of integers. The list is in the form firstRangeStart, firstRangeEnd, 
   * secondRangeStart, secondRangeEnd, etc. Each regular expression is 0-left-padded,
   * if necessary, to match strings of the given width.
   * @param pairs
   * @param minWidth
   * @return
   */
  private List<String> toRegex(List<Integer> pairs, int minWidth)
  {
    List<String> list = new ArrayList<>();
    String numberWithWidth = String.format("%%0%dd", minWidth);
    for (Iterator<Integer> iterator = pairs.iterator(); iterator.hasNext();)
    {
      String start = String.format(numberWithWidth, iterator.next()); // String.valueOf(iterator.next());
      String end = String.format(numberWithWidth, iterator.next());

      list.add(toRegex(start, end));
    }
    return list;
  }

  /**
   * return a regular expression string that matches the range
   * with the given start and end strings.
   * @param start
   * @param end
   * @return
   */
  private String toRegex(String start, String end)
  {
    assert start.length() == end.length();

    StringBuilder result = new StringBuilder();

    for (int pos = 0; pos < start.length(); pos++)
    {
      if (start.charAt(pos) == end.charAt(pos))
      {
        result.append(start.charAt(pos));
      } else
      {
        result.append('[').append(start.charAt(pos)).append('-')
            .append(end.charAt(pos)).append(']');
      }
    }
    return result.toString();
  }

  /**
   * Return the integer at the end of the range that is not covered
   * by any pairs added to the list.
   * @param rightPairs
   * @param start
   * @param end
   * @return
   */
  private int fillRightPairs(List<Integer> rightPairs, int start, int end)
  {
    int firstBeginRange = end;    // the end of the range not covered by pairs
                                  // from this routine.
    int y = end;
    int x = getPreviousBeginRange(y);

    while (x >= start)
    {
      rightPairs.add(y);
      rightPairs.add(x);
      y = x - 1;
      firstBeginRange = y;
      x = getPreviousBeginRange(y);
    }
    Collections.reverse(rightPairs);
    return firstBeginRange;
  }

  /**
   * Return the integer at the start of the range that is not covered
   * by any pairs added to its list. 
   * @param leftInts
   * @param start
   * @param end
   * @return
   */
  private int fillLeftPairs(ArrayList<Integer> leftInts, int start, int end)
  {
    int x = start;
    int y = getNextLeftEndRange(x);

    while (y < end)
    {
      leftInts.add(x);
      leftInts.add(y);
      x = y + 1;
      y = getNextLeftEndRange(x);
    }
    return x;
  }

  /**
   * given a number, return the number altered such
   * that any 9s at the end of the number remain, and
   * one more 9 replaces the number before the other
   * 9s.
   * @param num
   * @return
   */
  private int getNextLeftEndRange(int num)
  {
    char[] chars = String.valueOf(num).toCharArray();
    for (int i = chars.length - 1; i >= 0; i--)
    {
      if (chars[i] == '0')
      {
        chars[i] = '9';
      } else
      {
        chars[i] = '9';
        break;
      }
    }

    return Integer.parseInt(String.valueOf(chars));
  }

  /**
   * given a number, return the number altered such that
   * any 9 at the end of the number is replaced by a 0,
   * and the number preceding any 9s is also replaced by
   * a 0.
   * @param num
   * @return
   */
  private int getPreviousBeginRange(int num)
  {
    char[] chars = String.valueOf(num).toCharArray();
    for (int i = chars.length - 1; i >= 0; i--)
    {
      if (chars[i] == '9')
      {
        chars[i] = '0';
      } else
      {
        chars[i] = '0';
        break;
      }
    }

    return Integer.parseInt(String.valueOf(chars));
  }
}

这个是正确的,因为我已经能够测试它; bezmax发布的那个不太合适,虽然他有一个正确的想法(我也想出了)一个整体算法,一个主要的实现细节或两个有用的,所以我离开'回答'复选标记他的回应。

我对这个产生的兴趣感到有些惊讶,尽管不像问题的复杂程度那么多。

答案 2 :(得分:2)

这是python中的递归解决方案,适用于任意范围的正数。我们的想法是将范围划分为三个子范围:

  • 从开始到10的下一个倍数(如果开始不是10的倍数)
  • 从10的最后一个到结尾(如果结束不是10的倍数)
  • 这两个10的倍数之间的范围可以通过取下最后一个数字然后将正则表达式[0-9]添加到所有生成的正则表达式来递归处理

以下代码甚至可以优化[1-1]1等单个值的范围。要调用的函数是genRangeRegex(start是包含的,end是独占的):

def regexRangeDigits(start,stop):
  if start == stop:
    return str(start)
  return '[%d-%d]' % (start,stop)

# generate list of regular expressions for the number range [start,end[
def genRangeRegex(start, end):
  if start <= 0:
    raise ValueError('only ranges of positive numbers supported')

  print 'getting regex list for range [%d,%d[' % (start,end)
  if start >= end:
    return []

  digitsStart = str(start)
  digitsEnd   = str(end)
  lastDigitStart = start%10

  if start//10 == (end-1)//10: # integer division
    lastDigitStop = (end-1)%10
    regexAll = digitsStart[:-1] + regexRangeDigits(lastDigitStart,lastDigitStop)
    print '  regexAll   = %s' % regexAll
    return [regexAll]

  regexListStart = [] # at most one regular expression for going up to first multiple of 10
  if lastDigitStart != 0:
    regexStart = digitsStart[:-1] + regexRangeDigits(lastDigitStart,9)
    print '  regexStart = %s' % regexStart
    regexListStart.append(regexStart)

  regexListEnd = [] # at most one regular expression for going up from last multiple of 10
  lastDigitEnd = end%10
  if lastDigitEnd != 0:
    regexEnd = digitsEnd[:-1] + regexRangeDigits(0,lastDigitEnd-1)
    print '  regexEnd   = %s' % regexEnd
    regexListEnd.append(regexEnd)

  regexListMidTrunc = genRangeRegex((start+9)//10, end//10)
  regexListMid = [r+'[0-9]' for r in regexListMidTrunc]

  return regexListStart + regexListMid + regexListEnd

这里有一个示例输出函数的工作原理:

>>> genRangeRegex(12,231)
getting regex list for range [12,231[
  regexStart = 1[2-9]
  regexEnd   = 230
getting regex list for range [2,23[
  regexStart = [2-9]
  regexEnd   = 2[0-2]
getting regex list for range [1,2[
  regexAll   = 1
['1[2-9]', '[2-9][0-9]', '1[0-9][0-9]', '2[0-2][0-9]', '230']

答案 3 :(得分:2)

您无法仅使用角色组来满足您的要求。想象一下范围129-131。模式1[2-3][1-9]也会匹配超出范围的139

因此,在此示例中,您需要将最后一个组更改为其他组:1[2-3](1|9)。您现在可以找到这种效果以及数十和几个怪物,从而导致aapattern基本上将每个有效数字表示为固定数字序列的问题是唯一可行的解​​决方案。 (如果您不想要一个需要跟踪溢出的算法,以决定是否应该使用[2-8](8,9,0,1,2)

如果你无论如何自动生成模式 - 保持简单:

128-132

可以写成(为了更好的可读性,我省略了不匹配的组添加?:

(128|129|130|131|132)

算法应该是ovious,a for,array,string concatenation and join。

这已经按预期工作了,但你也可以执行一些&#34;优化&#34;如果你喜欢它更紧凑:

(128|129|130|131|132) <=>
1(28|29|30|31|32) <=>
1(2(8|9)|3(0|1|2))

更多优化

1(2([8-9])|3([0-2]))

最后一步的算法就在那里,寻找因子分解。一种简单的方法是将所有数字推送到树上,具体取决于字符位置:

1
  2
    8
    9
  3
    0
    1
    2

最后遍历三个并形成模式1(2(8|9)|3(0|1|2))。最后一步,将(a|(b|)*?c)模式中的任何内容替换为[a-c]

同样适用于11-29

11-29 <=>
(11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29) <=>   
(1(1|2|3|4|5|7|8|9)|2(1|2|3|4|5|7|8|9)) <=>
(1([1-9])|2([1-9]) 

作为补充,您现在可以继续进行分解:

(1([1-9])|2([1-9]) <=>
(1|2)[1-9] <=>
[1-2][1-9]

答案 4 :(得分:2)

[提示:不知怎的,我在第一个答案(使用Python)中提出的应用递归的想法没有达到OP,所以这里再次使用Java。请注意,对于递归解决方案,通常更容易证明正确性。]

使用递归的关键观察是以0结尾的数字开头并以9结尾的数字结尾的范围由数字模式覆盖,所有数字模式都以[0-9]结尾。

20-239 is covered by [2-9][0-9], 1[0-9][0-9], 2[0-3][0-9]

当取下范围的开始和结束的最后一位数时,结果范围由相同的数字模式覆盖,除了缺少的尾随[0-9]

20-239 is covered by [2-9][0-9], 1[0-9][0-9], 2[0-3][0-9]
2 -23  is covered by [2-9],      1[0-9],      2[0-3]

因此,当我们寻找覆盖范围的数字模式(例如13-247)时,我们会在第一个以0结尾的数字和最后一个数字结尾之后的范围之间拆分范围9(请注意,这些拆分范围可以为空),例如

13-247 = 13-19, 20-239, 240-247
20-247 =        20-239, 240-247
13-239 = 13-19, 20-239
20-239 =        20-239

通过取下最后的数字并将[0-9]附加到缩小范围的所有数字模式,递归处理剩余范围。

当为一个数字模式可以覆盖的子范围生成对start,end时(由bezmax和OP完成),缩小范围的子范围必须相应地“被炸毁”。

当范围内0没有数字结尾或者范围内9没有数字结尾的特殊情况时,只有在最后一位数字的起点和终点不同时才会出现这种特殊情况;在这种情况下,整个范围可以用一个数字模式覆盖。

所以这是基于这种递归原理的getRegexPairs的替代实现:

private static List<Integer> getRegexPairs(int start, int end)
{
  List<Integer> pairs = new ArrayList<>();   
  if (start > end) return pairs; // empty range
  int firstEndingWith0 = 10*((start+9)/10); // first number ending with 0
  if (firstEndingWith0 > end) // not in range?
  {
    // start and end differ only at last digit
    pairs.add(start);
    pairs.add(end);
    return pairs;
  }

  if (start < firstEndingWith0) // start is not ending in 0
  {
    pairs.add(start);
    pairs.add(firstEndingWith0-1);
  }

  int lastEndingWith9 = 10*(end/10)-1; // last number in range ending with 9
  // all regex for the range [firstEndingWith0,lastEndingWith9] end with [0-9]
  List<Integer> pairsMiddle = getRegexPairs(firstEndingWith0/10, lastEndingWith9/10);
  for (int i=0; i<pairsMiddle.size(); i+=2)
  {
    // blow up each pair by adding all possibilities for appended digit
    pairs.add(pairsMiddle.get(i)  *10+0);
    pairs.add(pairsMiddle.get(i+1)*10+9);
  }

  if (lastEndingWith9 < end) // end is not ending in 9
  {
    pairs.add(lastEndingWith9+1);
    pairs.add(end);
  }

  return pairs;
}

答案 5 :(得分:0)

一个选项是(对于范围[n,m])生成正则表达式n|n+1|...|m-1|m。但是,我认为你在获得更优化的东西之后。你仍然可以做同样的事情,生成一个FSM,通过状态机使用不同的路径匹配每个数字,然后使用任何众所周知的FSM最小化算法生成一个较小的机器,然后将其转换为更精简的正则表达式(因为没有Perl扩展的“正则表达式”与有限状态机同构)。

假设我们正在研究范围[107,112]:

state1:
  1 -> state2
  * -> NotOK
state2:
  0 -> state2.0
  1 -> state2.1
  * -> NotOK
state2.0:
  7 -> OK
  8 -> OK
  9 -> OK
  * -> NotOK
state2.1:
  0 -> OK
  1 -> OK
  2 -> OK
  * -> NotOK

我们无法真正进一步减少这台机器。我们可以看到state2.0对应于RE [789]而2.1对应于[012]。然后我们可以看到state2.0是(0[789])|(1[012]),整体是1(0[789])|(1[012])

可以在维基百科上找到关于DFA minimization的进一步阅读(以及从那里链接的页面)。

答案 6 :(得分:0)

如果你发现正则表达式模式范围在5到300之间,也支持浮点数;我创造了最好的答案......

^0*(([5-9]([.][0-9]{1,2})?)|[1-9][0-9]{1}?([.][0-9]{1,2})?|[12][0-9][0-9]([.][0-9]{1,2})?|300([.]0{1,2})?)$

1至300范围

^0*([1-9][0-9]?([.][0-9]{1,2})?|[12][0-9][0-9]([.][0-9]{1,2})?|300([.]0{1,2})?)$

答案 7 :(得分:0)

Bezmax的答案很接近,但不能完全解决问题。我相信它有一些细节不正确。我已经解决了问题,并用c ++编写了算法。 Bezmax算法的主要问题如下:

prev函数应产生以下内容: 387-> 380,379-> 300,299-> 100,99-> 10,9-> 0 而Bezmax有: 387-> 380,379-> 300,299-> 0

Bezmax的299“弱化”为0,在某些情况下可能会超出部分范围。基本上,您希望减弱到最低数量,但从不更改位数。完整的解决方案太多的代码无法在此处发布,但此处是重要的部分。希望这对某人有帮助。

    // Find the next number that is advantageous for regular expressions.
    //
    // Starting at the right most decimal digit convert all zeros to nines. Upon
    // encountering the first non-zero convert it to a nine and stop. The output
    // always has the number of digits as the input.
    // examples: 100->999, 0->9, 5->9, 9->9, 14->19, 120->199, 10010->10099
    static int Next(int val)
    {
       assert(val >= 0);

       // keep track of how many nines to add to val.
       int addNines = 0;

       do {
          auto res = std::div(val, 10);
          val = res.quot;
          ++addNines;
          if (res.rem != 0) {
             break;
          }
       } while (val != 0);

       // add the nines
       for (int i = 0; i < addNines; ++i) {
          val = val * 10 + 9;
       }

       return val;
    }

    // Find the previous number that is advantageous for regular expressions.
    //
    // If the number is a single digit number convert it to zero and stop. Else...
    // Starting at the right most decimal digit convert all trailing 9's to 0's
    // unless the digit is the most significant digit - change that 9 to a 1. Upon
    // encounter with first non-nine digit convert it to a zero (or 1 if most
    // significant digit) and stop. The output always has the same number of digits
    // as the input.
    // examples: 0->0, 1->0, 29->10, 999->100, 10199->10000, 10->10, 399->100
    static int Prev(int val)
    {
       assert(val >= 0);

       // special case all single digit numbers reduce to 0
       if (val < 10) {
          return 0;
       }

       // keep track of how many zeros to add to val.
       int addZeros = 0;

       for (;;) {
          auto res = std::div(val, 10);
          val = res.quot;
          ++addZeros;
          if (res.rem != 9) {
             break;
          }

          if (val < 10) {
             val = 1;
             break;
          }
       }

       // add the zeros
       for (int i = 0; i < addZeros; ++i) {
          val *= 10;
       }

       return val;
    }

    // Create a vector of ranges that covers [start, end] that is advantageous for
    // regular expression creation. Must satisfy end>=start>=0.
    static std::vector<std::pair<int, int>> MakeRegexRangeVector(const int start,
                                                                 const int end)
    {
       assert(start <= end);
       assert(start >= 0);

       // keep track of the remaining portion of the range not yet placed into
       // the forward and reverse vectors.
       int remainingStart = start;
       int remainingEnd = end;

       std::vector<std::pair<int, int>> forward;
       while (remainingStart <= remainingEnd) {
          auto nextNum = Next(remainingStart);
          // is the next number within the range still needed.
          if (nextNum <= remainingEnd) {
             forward.emplace_back(remainingStart, nextNum);
             // increase remainingStart as portions of the numeric range are
             // transfered to the forward vector.
             remainingStart = nextNum + 1;
          } else {
             break;
          }
       }
       std::vector<std::pair<int, int>> reverse;
       while (remainingEnd >= remainingStart) {
          auto prevNum = Prev(remainingEnd);
          // is the previous number within the range still needed.
          if (prevNum >= remainingStart) {
             reverse.emplace_back(prevNum, remainingEnd);
             // reduce remainingEnd as portions of the numeric range are transfered
             // to the reverse vector.
             remainingEnd = prevNum - 1;
          } else {
             break;
          }
       }

       // is there any part of the range not accounted for in the forward and
       // reverse vectors?
       if (remainingStart <= remainingEnd) {
          // add the unaccounted for part - this is guaranteed to be expressable
          // as a single regex substring.
          forward.emplace_back(remainingStart, remainingEnd);
       }

       // Concatenate, in reverse order, the reverse vector to forward.
       forward.insert(forward.end(), reverse.rbegin(), reverse.rend());

       // Some sanity checks.
       // size must be non zero.
       assert(forward.size() > 0);

       // verify starting and ending points of the range
       assert(forward.front().first == start);
       assert(forward.back().second == end);

       return forward;
    }

答案 8 :(得分:0)

我最近有一个需求,我需要使用Java开发“用于数字范围的正则表达式生成器”,并且我已经开发了完整的解决方案,该解决方案发布在IdeOne上,并且类名为ideone.java-Source Code on ideone.com 。关于ideone的代码受到了严重评论,它使用的算法与其他用户发布的算法相同,因此,我仅着重介绍所做的更改和功能或已解决的问题。我使用了Bezmax(概念),arcy(整体代码和将RegEx范围生成为对的想法)和coproc(使用递归生成RegEx对的方法,而不是Bezmax(概念)的答案中提供的部分解决方案。弧形)。感谢这三个人。

Ideone.java提供了两个公共方法,它们实现RegExGenerator逻辑-一个接受字符串数字范围,另一个接受整数范围。

generateRegEx(String begStr, String endStr)
generateRegEx(int beg, int end)

上述两种公共方法都调用generateRegExCommon方法,该方法又调用getRegExPairsRecursion方法(与coproc的答案提供的实现相同),以生成一个包含成对数字的列表,这些数字成对表示有效RegEx范围的下限和上限。然后,它调用formatPairsToRegEx方法将RegEx对转换为实际RegEx范围,如果输入中包含零,则该RegEx范围可能包含零前缀以匹配输入长度。如果输入不包含前导零或使用整数输入,则不会将前导零添加到输出范围。输出可以作为字符串数组的列表使用,其中每个条目/元素都是有效的正则表达式数字范围:

regexArray - String Array where each element is a valid regular expression range.
regexList  - List of String elements where each element is a valid regular expression range.

下图显示了Java代码(ideone.java)的示例执行的顺序图,每个阶段都有输入和输出。使用的示例的数字字符串的输入范围带有前导零,其中下限范围值为“ 0006”,上限范围为“ 0977”。输出如下图所示:

000[6-9]
00[1-9][0-9]
0[1-8][0-9][0-9]
09[0-6][0-9]
097[0-7]

RegEx Number Range Generator

代码具有以下优点:

  • 一个综合了所有答案和算法的综合解决方案。
  • 打印语句,可帮助调试代码,并且可以轻松地转换为任何日志记录框架跟踪/调试语句,而不是System.out。
  • 修复了0的低范围不适用于其他答案的问题。