【问题标题】:Splitting string by a colon that is NOT contained within parenthesis ()用不包含在括号 () 中的冒号分割字符串
【发布时间】:2011-04-23 17:57:18
【问题描述】:

我正在开发一个 HTML5/JavaScript 游戏引擎,并且我开始遇到一个我过去从未遇到过的场景,并且不知道我该如何解决这个问题。

简单地说,我想通过一个字符将一个字符串拆分为一个数组 - 只要该字符不在括号内。

基本上,在项目/图块之类的 XML 文件中,我存储“触发器”,它们是为代码将执行的操作提供规则的语句。单个触发器的不同参数用冒号 (:) 分隔,一个项目可以有多个触发器,每个触发器由逗号分隔。这是一个例子:

<response trigger="npc:self:dialog:1:3">No, thank you.</response>

(这基本上是说:如果选择了这个响应,则使最初提问的NPC循环到特定转换的特定消息)

继续前进:我开始需要将回调触发器封装在带有某些触发器的参数括号内的能力。这是一个例子:

<response trigger="shop:open:1:(npc:self:dialog:1:4)">Yes, please.</response>

(这基本上是说:打开一个特定的商店,当商店关闭时,跳转到一个特定的对话/消息给说话的NPC)

这个想法是,当商店关闭时,我可以调用该触发器的第四个参数(它本身就是一个触发器)。我相信您已经猜到了,这里的问题是,如果我根据“:”拆分初始触发器字符串,那么它会将回调触发器分解为主触发器的其他(混乱)参数。我不想要那个。也不,我不想做任何事情,比如将次要触发器由另一个角色拆分(出于稍后生成的原因,并且因为我想有时我会想要在更深层次上嵌套很多触发器并且我不想使用不同的字符。我知道变通方法,但我想学习用不包含在其他特定字符中的字符分割的正确方法。

由于我用括号封装回调参数,我认为必须有一个干净的正则表达式,我可以使用所有冒号来分割主触发器字符串,而不是在括号内。

很遗憾,我无法想出正确的表达方式来完成这项工作。

有什么想法吗?

非常感谢你们中的任何人可能获得的任何帮助。 :)

【问题讨论】:

  • 确定是否可以使用正则表达式解析的关键问题:括号可以嵌套吗?
  • @cobbal:我会说是的,引用“我想有时我会想要在更深层次嵌套很多触发器”。
  • @ChristianSemrau 啊,我似乎错过了这一点。
  • 离题建议:使用JSON。你的生活会更轻松。
  • @Zirak 在这里使用 JSON 不会有任何区别 - JSON 只会将 XML 替换为结构容器,而我正在处理的数据是一个字符串(并且应该保持不变)。如果我将触发器构造为 XML(出于各种原因我不会这样做),那么它可能是相关的。郑重声明,我不同意 JSON 与 XML 之战; XML 的额外膨胀对于乍一看结构上可读的数据容器来说是一个很小的代价(特别是因为我现在手动编写这些数据文件)。

标签: javascript regex arrays split


【解决方案1】:

我怀疑你不能,至少如果有嵌套括号的可能性,因为识别正确的括号嵌套是不规则的。

在任何情况下,与其构造一些巴洛克式的正则表达式,不如考虑一个非常简单的解析器:扫描到下一个出现的“:”或“(”,然后对下一个标记做一些事情。重复。这很容易与递归下降有关,看起来像

parse(string)
   if string is empty: return
   scan to delimiter, put delimiter index into d, token string into t
   put t into a table for processing later
   case on d:
      string[d] == ":": parseColonToken(string[d+1:])
      string[d] == "(": parseParentString(strin[d+1:])
   end
end

(显然这是伪代码。以string[n:]为“string从索引n到末尾的子字符串。)

考虑一下,您可能只需以 parseColonToken 开头,但我不确定这是否符合您的预期语法。

【讨论】:

  • 我现在感觉你也做不到,因为我花了很长时间编写失败的表达式,然后你们中的前两个似乎很确定这甚至不是正则表达式场景.我很欣赏示例代码 - 我将尝试编写某种解析器。我想我对单线的希望有点不切实际。 >_
  • 假设括号可以嵌套,那么这是一个你不能嵌套的定理。老实说,递归下降的事情是如此简单,我整个下午都在考虑它的其他用途。
【解决方案2】:

您无法为您的问题找到正则表达式是有充分理由的:

您描述的语言是非正则,即无法用正则表达式解析

基本上,您必须解析括号结构以确定所有括号之外的冒号。这对于正则表达式是不可能的。

嵌套括号的语言是上下文无关的[1],因此可以直接编写递归解析器。

[1]http://en.wikipedia.org/wiki/Context-free_language

补充:您不需要递归解析器,括号嵌套级别的简单计数器就足够了:

// Pseudo code
int depth = 0;
List<int> breakIndices;
for int index = 0 .. input.length-1:
  switch(input[index])
    ':': if (depth==0) breakIndices.add(index); break;
    '(': depth++; break;
    ')': depth--; break;
    default: break;
// Now, all indices of the colons you need are in the breakIndices list.

【讨论】:

  • 感谢您的洞察力和示例方法。我承认,也许我是一厢情愿的想法,因为单线可以实现,所以我将使用您的示例(以及此处提供的另一个示例)并将某些东西放在一起。 - 再次感谢您解释为什么这甚至不是正则表达式场景。
  • 确实如此。但我知道没有证据表明可以解析嵌套括号的语言的扩展正则表达式引擎。
【解决方案3】:

我认为最简单的方法是将字符串分成“函数”部分和“参数”部分,然后分别处理这两个部分。如果要保留参数部分的括号,则:

var parts1 = "shop:open:1:(npc:self:dialog:1:4)".split(/:(?=\()/);
// parts1 now looks like ["shop:open:1", "(npc:self:dialog:1:4)"]
var parts2 = "shop:open:1".split(/:(?=\()/);
// parts2 now looks like ["shop:open:1"]

然后:

var cmd = null;
var arg = null;
if(parts.length > 0) {
    cmd = parts[0].split(':');
    arg = (parts[1] || '').replace(/[()]/g, '').split(':');
}

您也许可以将更多内容塞进一个正则表达式中(可能所有这些都取决于您的目标正则表达式引擎支持哪些非常规功能),但没有什么意义,而且清晰是您的代码更好的目标“短的”。如果手头有decent JavaScript regex reference,任何查看上述内容的人都应该能够弄清楚它在做什么。

如果您最终使用引用和转义等处理更复杂的表达式,那么您可以尝试修改 a CSV parser 来满足您的需要。

【讨论】:

    猜你喜欢
    • 2020-11-18
    • 1970-01-01
    • 2021-06-12
    • 1970-01-01
    • 1970-01-01
    • 2011-06-25
    • 2020-04-26
    • 2022-01-17
    • 2016-01-07
    相关资源
    最近更新 更多