任何列表上正则表达式的泛化

时间:2010-11-29 11:30:54

标签: regex

我正在使用包含整数的大型列表,我想对它们进行一些模式匹配(比如查找某些序列)。正则表达式是最合适的,除了它们似乎总是只处理字符列表,a.k.a。字符串。是否有任何库(使用任何语言)可以处理任意类型的列表?

我知道我可以将整数列表转换为字符串,然后进行正常的正则表达式搜索,但这看起来有点浪费和不优雅。

修改

我的要求相当简单。不需要嵌套列表,不需要花哨的字符类。基本上我只对可能变得非常复杂的序列的出现感兴趣。 (例如"[abc]{3,}.([ab]?[^a]{4,7})"abc为整数的内容。这应该可以概括为可以检查相等性的任何类型。对于可枚举类型,您还可以使用"[a-z]"之类的东西。

7 个答案:

答案 0 :(得分:2)

正则表达式仅匹配字符串by definition

当然,理论上你可以构造一个等价的语法,比如数字列表。对于偶数,\e为新标记,奇数为\o,方数为\s,实数为\r等,所以

[1, 2, 3, 4, 5, 6]

将匹配

^(\o\e)*$

[ln(3), math.pi, sqrt(-1)]

将匹配

^\R*$

等。听起来像一个有趣的项目,但也像一个非常困难的项目。如何扩展以处理任意列表,嵌套和所有,都超出了我的范围。

答案 1 :(得分:1)

一些正则表达式语法概括为泛型序列。此外,为了能够指定任何对象,字符串不是表达式本身的最佳媒介。

python中的“小”示例:

def choice(*items):
  return ('choice',[value(item) for item in items])

def seq(*items):
  return ('seq',[value(item) for item in items])

def repeat(min,max,lazy,item):
  return ('repeat',min,max,lazy,value(item))

def value(item):
  if not isinstance(item,tuple):
    return ('value',item)
  return item

def compile(pattern):
  ret = []
  key = pattern[0]
  if key == 'value':
    ret.append(('literal',pattern[1]))
  elif key == 'seq':
    for subpattern in pattern[1]:
      ret.extend(compile(subpattern))
  elif key == 'choice':
    jumps = []
    n = len(pattern[1])
    for i,subpattern in enumerate(pattern[1]):
      if i < n-1:
        pos = len(ret)
        ret.append('placeholder for choice')
        ret.extend(compile(subpattern))
        jumps.append(len(ret))
        ret.append('placeholder for jump')
        ret[pos] = ('choice',len(ret)-pos)
      else:
        ret.extend(compile(subpattern))
    for pos in jumps:
      ret[pos] = ('jump', len(ret)-pos)
  elif key == 'repeat':
    min,max,lazy,subpattern = pattern[1:]
    for _ in xrange(min):
      ret.extend(compile(subpattern))
    if max == -1:
      if lazy:
        pos = len(ret)
        ret.append('placeholder for jump')
        ret.extend(compile(subpattern))
        ret[pos] = ('jump',len(ret)-pos)
        ret.append(('choice',pos+1-len(ret)))
      else:
        pos = len(ret)
        ret.append('placeholder for choice')
        ret.extend(compile(subpattern))
        ret.append(('jump',pos-len(ret)))
        ret[pos] = ('choice',len(ret)-pos)
    elif max > min:
      if lazy:
        jumps = []
        for _ in xrange(min,max):
          ret.append(('choice',2))
          jumps.append(len(ret))
          ret.append('placeholder for jump')
          ret.extend(compile(subpattern))
        for pos in jumps:
          ret[pos] = ('jump', len(ret)-pos)
      else:
        choices = []
        for _ in xrange(min,max):
          choices.append(len(ret))
          ret.append('placeholder for choice')
          ret.extend(compile(subpattern))
          ret.append(('drop,'))
        for pos in choices:
          ret[pos] = ('choice',len(ret)-pos)
  return ret

def match(pattern,subject,start=0):
  stack = []
  pos = start
  i = 0
  while i < len(pattern):
    instruction = pattern[i]
    key = instruction[0]
    if key == 'literal':
      if pos < len(subject) and subject[pos] == instruction[1]:
        i += 1
        pos += 1
        continue
    elif key == 'jump':
      i += instruction[1]
      continue
    elif key == 'choice':
      stack.append((i+instruction[1],pos))
      i += 1
      continue
    # fail
    if not stack:
      return None
    i,pos = stack.pop()
  return pos

def find(pattern,subject,start=0):
  for pos1 in xrange(start,len(subject)+1):
    pos2 = match(pattern,subject,pos1)
    if pos2 is not None: return pos1,pos2
  return None,None

def find_all(pattern,subject,start=0):
  matches = []
  pos1,pos2 = find(pattern,subject,start)
  while pos1 is not None:
    matches.append((pos1,pos2))
    pos1,pos2 = find(pattern,subject,pos2)
  return matches

# Timestamps: ([01][0-9]|2[0-3])[0-5][0-9]
pattern = compile(
  seq(
    choice(
      seq(choice(0,1),choice(0,1,2,3,4,5,6,7,8,9)),
      seq(2,choice(0,1,2,3)),
    ),
    choice(0,1,2,3,4,5),
    choice(0,1,2,3,4,5,6,7,8,9),
  )
)

print find_all(pattern,[1,3,2,5,6,3,4,2,4,3,2,2,3,6,6,5,3,5,3,3,2,5,4,5])
# matches: (0,4): [1,3,2,5]; (10,14): [2,2,3,6]

有几点改进:

  • 更多构造:带否定的范围,范围
  • 类而不是元组

答案 2 :(得分:0)

如果你真的需要像正则表达式那样的免费语法,那么你必须按照Tim的回答中所描述的那样。 如果您只搜索固定数量的模式,那么最简单,最快捷的方法就是编写自己的搜索/过滤功能。

答案 3 :(得分:0)

确实有趣的问题。

横向思维:下载.Net Framework Source code,解除Regex源代码并使其适应整数而不是字符。

只是一个想法。

答案 4 :(得分:0)

嗯,Erlang已经内置了模式匹配(你的类型)。我在Ruby中做了类似的事情 - 有点可能不太好的hackery,请参阅http://radiospiel.org/0x16-its-a-bird

答案 5 :(得分:0)

从1.9版开始,Clojure在标准库中有clojure.spec,可以做到这一点,甚至更多。例如,描述一个可能以一个偶数结尾的奇数序列:

(require '[clojure.spec.alpha :as s])
(s/def ::odds-then-maybe-even (s/cat :odds (s/+ odd?)
                                     :even (s/? even?)))

然后要获得匹配的子序列,您将执行以下操作:

(s/conform ::odds-then-maybe-even [1 3 5 100])
;;=> {:odds [1 3 5], :even 100}

(s/conform ::odds-then-maybe-even [1])
;;=> {:odds [1]}

并找出为什么序列与您的定义不匹配的原因:

(s/explain ::odds-then-maybe-even [100])
;; In: [0] val: 100 fails spec: ::odds-then-maybe-even at: [:odds] predicate: odd?

https://clojure.org/guides/spec

上查看带有示例的完整文档

答案 6 :(得分:-2)

您可以尝试pamatcher,它是一个JavaScript库,可以为任何类型的项目(任何类型)概括正则表达式的概念。

&#34; [abc] {3,}。([ab]?[^ a] {4,7})&#34;模式匹配,其中a,b,c是整数:

var pamatcher = require('pamatcher');

var a = 10;
var b = 20;
var c = 30;

var matcher = pamatcher([
  { repeat: 
      { or: [a, b, c] }, 
    min: 3
  },
  () => true,
  { optional: 
    { or: [a, b] } 
  },
  { repeat: (i) => i != a,
    min: 4,
    max: 7
  }
]);

var input = [1, 4, 8, 44, 55];
var result = matcher.test(input);
if(result) {
  console.log("Pattern matches!");
} else {
  console.log("Pattern doesn't match.");
}

注意:我是这个图书馆的创建者。