从正则表达式的子集生成所有匹配项

时间:2013-02-10 13:45:51

标签: regex

我需要定义一堆矢量序列,它们是左,下,右,上的一系列LDRUx休息。有可选部件和/或部件。我一直在使用我自己发明的系统来记录它,但我想将其记录下来供其他可能非程序员阅读。

我现在想要使用正则表达式的子集(我不打算使用任何通配符或无限重复)来定义向量序列和脚本以生成所有可能的匹配字符串......

/LDR/ produces ['LDR']
/LDU?R/ produces ['LDR','LDUR']
/R(LD|DR)U/ produces ['RLDU','RDRU']
/DxR[DL]U?RDRU?/ produces ['DxRDRDR','DxRDRDRU','DxRDURDR','DxRDURDRU','DxRLRDR','DxRLRDRU','DxRLURDR','DxRLURDRU']

我是否可以使用现有的库来生成 所有匹配

修改

我意识到我只需要 语句,因为thing or nothing可以选择a或b指定可选项,两者都可以{ {1}}。我可以使用另一种语言来定义我想要做的事情吗?

3 个答案:

答案 0 :(得分:2)

通过将@Dukeling提供的链接的java代码翻译成javascript,我想我已经解决了我的问题......

var Node = function(str){
    this.bracket = false;
    this.children = [];
    this.s = str;
    this.next = null;
    this.addChild = function(child){
        this.children.push(child);
    }
}

var printTree = function(root,prefix){
  prefix = prefix.replace(/\./g, "");
  for(i in root.children){
    var child = root.children[i]
    printTree(child, prefix + root.s);
  }
  if(root.children.length < 1){
    console.log(prefix + root.s);
  }
}

var Stack = function(){
    this.arr = []
    this.push = function(item){
        this.arr.push(item)
    }
    this.pop = function(){
        return this.arr.pop()
    }
    this.peek = function(){
        return this.arr[this.arr.length-1]
    }
}

var createTree = function(s){

    // this line was causing errors for `a(((b|c)d)e)f` because the `(((` was only
    // replacing the forst two brackets.
    // var s = s.replace(/(\(|\||\))(\(|\||\))/g, "$1.$2");
    // this line fixes it
    var s = s.replace(/[(|)]+/g, function(x){ return x.split('').join('.') });

    var str = s.split('');
    var stack = new Stack();
    var root = new Node("");
    stack.push(root); // start node
    var justFinishedBrackets = false;
    for(i in str){
        var c = str[i]
        if(c == '('){
            stack.peek().next = new Node("Y"); // node after brackets
            stack.peek().bracket = true; // node before brackets
        } else if (c == '|' || c == ')'){
            var last = stack.peek(); // for (ab|cd)e, remember b / d so we can add child e to it
            while (!stack.peek().bracket){ // while not node before brackets
                stack.pop();
            }
            last.addChild(stack.peek().next); // for (b|c)d, add d as child to b / c
        } else {
            if (justFinishedBrackets){
                var next = stack.pop().next;
                next.s = "" + c;
                stack.push(next);
            } else {
                var n = new Node(""+c);
                stack.peek().addChild(n);
                stack.push(n);
            }
        }
        justFinishedBrackets = (c == ')');
    }
    return root;
}

// Test it out
var str = "a(c|mo(r|l))e";
var root = createTree(str);
printTree(root, "");
// Prints: ace / amore / amole

我只更改了一行,允许处理两个以上的连续括号,并在评论中保留原始翻译

我还添加了一个函数来返回结果数组,而不是打印它们......

var getTree = function(root,prefix){
  this.out = this.out || []
  prefix = prefix.replace(/\./g, "");
  for(i in root.children){
    var child = root.children[i]
    getTree(child, prefix + root.s, out);
  }
  if(root.children.length < 1){
    this.out.push(prefix + root.s);
  }
  if(!prefix && !root.s){
    var out = this.out;
    this.out = null
    return out;
  }
}

// Test it
var str = "a(b|c)d";
var root = createTree(str);
console.log(getTree(root, ""));
// logs ["abd","acd"]

最后一部分,也是为了允许空字符串,所以... (ab|c|)表示abcnothing,以及方便捷径,以便ab?c 1}}被翻译为a(b|)c

var getMatches = function(str){
    str = str.replace(/(.)\?/g,"($1|)")
    // replace all instances of `(???|)` with `(???|µ)`
    // the µ will be stripped out later
    str = str.replace(/\|\)/g,"|µ)")
    // fix issues where last character is `)` by inserting token `µ`
    // which will be stripped out later
    str = str+"µ"
    var root = createTree(str);
    var res = getTree(root, "");
    // strip out token µ
    for(i in res){
        res[i] = res[i].replace(/µ/g,"")
    }
    // return the array of results
    return res
}

getMatches("a(bc|de?)?f");
// Returns: ["abcf","adef","adf","af"]

最后一部分是一个小小的hack-ish,因为它依赖于µ不在字符串中(对我来说不是问题)并且解决了一个错误,其中一个)在最后输入字符串导致输出错误,方法是在每个字符串的末尾插入µ,然后从结果中删除它。我很乐意有人建议一个更好的方法来处理这些问题,因此它可以作为一个更通用的解决方案。

这段代码尽我所能。谢谢你的帮助!

答案 1 :(得分:1)

我想你用树做的事情很简单(只要它只是或者语句)。

a(b|c)d(或任何or-statement)解析为树,如下所示:a包含子bcb和{{1}有一个共同的孩子cdb都可以包含0个或更多节点(在c中可以是c,在这种情况下(部分)树将是g(e|f)h或者a -> g -> e/f (2 nodes) -> h -> d可以为空,在这种情况下(部分)树将是c,但实际的物理空节点可以简化在尝试编写代码时应该看到的内容。 / p>

使用递归或堆栈生成树应该不会太困难。

一旦你有了一棵树,递归迭代整个事物并生成所有字符串是微不足道的。

此外,here是指向类似问题的链接,提供一两个图书馆。

修改

a -> d - 好吧,也许不是

Here是一个有点复杂的例子(Java),可能需要一些关于堆栈的高级知识。

由于在每个"shouldn't be too difficult"(())等之间插入一个特殊字符,

Here是一个稍微简单的版本(Java)。

请注意,这些都不是特别有效,重点是了解这个想法。

答案 2 :(得分:1)

这是一个JavaScript示例,用于解析(a | b)和(a | b |)的可能性,创建可能的子串的数组,并根据this answer组成匹配。

var regex = /\([RLUD]*\|[RLUD]*\|?\)/, 
    str = "R(LD|DR)U(R|L|)",
    substrings = [], matches = [], str_tmp = str, find

while (find = regex.exec(str_tmp)){
  var index = find.index

  finds = find[0].split(/\|/)
  substrings.push(str_tmp.substr(0, index))

  if (find[0].match(/\|/g).length == 1) 
    substrings.push([finds[0].substr(1), finds[1].replace(/.$/, '')])
  else if (find[0].match(/\|/g).length == 2){
    substrings.push([finds[0].substr(1), ""])
    substrings.push([finds[1], ""])
  }

  str_tmp = str_tmp.substr(index + find[0].length)
}
if (str_tmp) substrings.push([str_tmp])
console.log(substrings) //>>["R", ["LD", "DR"], "U", ["R", ""], ["L", ""]]

//compose matches
function printBin(tree, soFar, iterations) {
  if (iterations == tree.length) matches.push(soFar)
  else if (tree[iterations].length == 2){
      printBin(tree, soFar + tree[iterations][0], iterations + 1)
      printBin(tree, soFar + tree[iterations][1], iterations + 1)
  }
  else printBin(tree, soFar + tree[iterations], iterations + 1)
}
printBin(substrings, "", 0)
console.log(matches) //>>["RLDURL", "RLDUR", "RLDUL", "RLDU", "RDRURL", "RDRUR", "RDRUL", "RDRU"]