关于sql:正则表达式查找数字是否在一个范围内,例如1,3,10-15,17

Regex to find if a number is within a range, example 1,3,10-15,17

本问题已经有最佳答案,请猛点这里访问。

我需要找到一种方法来检查给定的数字是否包含在一系列数字和范围中。

下面是一个例子:包含数字和范围的字符串是"1,3,10-15,17",问题可以是"13包括在列表中吗?"

在这种情况下,答案是"是",因为它在10-15范围内。例如第16个例子。

就我的目的而言,返回一些可以翻译为"是"或"否"的内容就足够了。

如有任何帮助,我们将不胜感激。

谢谢

===根据收到的答案修改。====

我最终的解决方案是基于Java存储过程+PL/SQL包装器。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
CREATE OR REPLACE AND compile java SOURCE named"RegExp" AS
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class REGEXP
{
  private static  Pattern P = Pattern.compile("(\\d+)-(\\d+)");

  public static INT inRange (INT p1, java.lang.String p2)
  {
    INT iReturn = -1;
         INT INPUT;

         String[] sa;
         sa = p2.split(",");
         Set<Integer> IS = NEW HashSet<Integer>();

         INPUT = p1;

        FOR ( String s : sa)
        {
             Matcher m = P.matcher(s);
            IF (m.find())
            {
                 INT START = INTEGER.parseInt(m.group(1));
                 INT END = INTEGER.parseInt(m.group(2));
                FOR (INT i = START; i <= END; i++)
                {
                    IS.add(i);
                }
            }
            ELSE
            {
                IS.add(INTEGER.parseInt(s));
            }
        }

        IF (IS.contains(INPUT))
        {
           // System.out.format("Found %d in the set %s", INPUT, IS);
            iReturn = 1;
        }
        ELSE
        {
           // System.out.format("Didn't find %d in set %s", INPUT, IS);
            iReturn = 0;
        }
        RETURN iReturn;

  }

};

这是PL/SQL包装器

1
2
3
CREATE OR REPLACE FUNCTION InRange (p1  IN  NUMBER, p2  IN  VARCHAR2) RETURN NUMBER
AS LANGUAGE JAVA
NAME 'RegExp.inRange (int, java.lang.String) return int';

下面是一个示例SQL语句:

1
SELECT InRange(13,'1,3,10-15,17') FROM   DUAL;

这将返回一(1),因为数字13的范围是10-15

以下返回零(0),因为6不在列表中

1
SELECT InRange(6,'1,3,10-15,17') FROM   DUAL;

===更新II====虽然我不是Java程序员——所以可以自由地增强代码——我确实创建了一个类似的简单函数来处理字符。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public static INT inRangeString (java.lang.String p1, java.lang.String p2)
  {
      INT iReturn = -1;


      String[] sa;
      sa = p2.split(",");

      Set<String> IS = NEW HashSet<String>();


      FOR ( String s : sa)
        {
                 IS.add(s);
        }

       IF (IS.contains(p1)) {
          iReturn = 1;  
              // VALUE IS valid  
          } ELSE {  
                    iReturn = 0;
              // VALUE IS invalid  
          }  

       RETURN iReturn;
  }


唯一的方法是将范围转换成模式(显然),因为您需要忘记处理整数,但只将数字视为"普通字符"。对于您的示例范围:

1
^(?:1[0-57]?|3)$

注意:regex显然不是检查整数是否在数字范围内的方法。在现实生活中,你会使用好的旧条件。


正则表达式是惯用解决方案的一部分。

但这绝不是唯一的部分。正则表达式用于模式匹配,而不是执行所需的任意逻辑。

惯用解决方案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import javax.annotation.Nonnull;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.google.common.collect.Range;
import com.google.common.collect.RangeSet;
import com.google.common.collect.TreeRangeSet;

public class Q25004732
{
    private static final Pattern RANGE = Pattern.compile("(?i)^(?=[a-z]+-[a-z]+$|\\d+-\\d+$)([a-z\\d]+)-([a-z\\d]+)$");

    public static void main(final String[] args)
    {
        final String[] sa ="1,3,10-15,17,A,b,XX-ZZ,z".split(",");
        final RangeSet<String> rs = TreeRangeSet.create();
        FOR (final String s : sa)
        {
            final Matcher m = RANGE.matcher(s);
            IF (m.find())
            {
                rs.add(Range.closed(m.group(1), m.group(2)));
            }
            ELSE
            {
                rs.add(Range.closed(s, s));
            }
        }
        report("13", rs);
        report("A", rs);
        report("XY", rs);
        report("c", rs);
        report("42", rs);
    }

    private static void report(@Nonnull final String INPUT, @Nonnull final RangeSet<String> rs)
    {
        IF (rs.contains(INPUT))
        {
            System.out.format("Found %s in the set %s", INPUT, rs);
        }
        ELSE
        {
            System.out.format("Didn't find %s in set %s", INPUT, rs);
        }
        System.out.println();
    }
}

预期输出:

1
2
3
4
5
Found 13 IN the SET [[11], [1015], [1717], [33], [A‥A], [XX‥ZZ], [b‥b], [z‥z]]
Found A IN the SET [[11], [1015], [1717], [33], [A‥A], [XX‥ZZ], [b‥b], [z‥z]]
Found XY IN the SET [[11], [1015], [1717], [33], [A‥A], [XX‥ZZ], [b‥b], [z‥z]]
Didn't find c in set [[1‥1], [10‥15], [17‥17], [3‥3], [A‥A], [XX‥ZZ], [b‥b], [z‥z]]
Didn'
t find 42 IN SET [[11], [1015], [1717], [33], [A‥A], [XX‥ZZ], [b‥b], [z‥z]]

取决于:

谷歌番石榴

1
2
3
4
5
<dependency>
    <groupId>com.google.guava</groupId>
    guava</artifactId>
    <version>[17.0,)</version>
</dependency>

静态分析工具

1
2
3
4
5
<dependency>
    <groupId>com.google.code.findbugs</groupId>
    findbugs</artifactId>
    <version>[3.0.0,)</version>
</dependency>

源代码和其他答案可以在我的StackOverflow Github存储库中找到。


这不可能只使用regex。您所能做的是使用regex(在本例中,这是一种过度杀戮)提取数字,将它们分为两类:数字和范围。然后您可以使用它来确定列表中是否有数字。