使用正则表达式解析带有转义字符的字符串

7

我正在从格式化的字符串读取信息。 该格式如下:

"foo:bar:beer:123::lol"

冒号(:)之间的所有数据是我想用正则表达式提取的内容。如果一个冒号后面跟着另一个冒号(如"::"),则其中的数据应为""(空字符串)。

目前,我正在使用以下正则表达式进行解析:

(.*?)(:|$)

现在我想到数据中可能会出现“:”符号,因此必须进行转义。 例如:
"foo:bar:beer:\::1337"

我该如何更改我的正则表达式,以便它也匹配"\:"作为数据呢?

编辑:我正在使用JavaScript作为编程语言。它似乎在处理复杂正则表达式方面存在一些限制。解决方案应该在JavaScript中有效。

谢谢, McFarlane

3个回答

4
var myregexp = /((?:\\.|[^\\:])*)(?::|$)/g;
var match = myregexp.exec(subject);
while (match != null) {
    for (var i = 0; i < match.length; i++) {
        // Add match[1] to the list of matches
    }
    match = myregexp.exec(subject);
}

输入:"foo:bar:beer:\\:::1337"

输出:["foo", "bar", "beer", "\\:", "", "1337", ""]

最后一个匹配总是空字符串。这是不可避免的,因为您还希望在分隔符之间匹配空字符串(并且 JavaScript 中缺少向后断言)。

解释:

(          # Match and capture:
 (?:       # Either match...
  \\.      # an escaped character
 |         # or
  [^\\:]   # any character except backslash or colon
 )*        # zero or more times
)          # End of capturing group
(?::|$)    # Match (but don't capture) a colon or end-of-string

我收到了以下输出: ["foo", "", "bar", "", "beer", "", ":", "", "", "1337", ""] - McFarlane
啊,没错。RegexBuddy足够“聪明”,可以省略掉多余的空匹配项,但JavaScript和Python(我现在正在测试)不能。让我们看看能否找到解决方案。 - Tim Pietzcker
你的更新示例按预期匹配数据,但仍然返回 ["", ""]。在你的示例中,循环将导致无限循环,因为匹配永远不会为null。我已将while循环限制为执行[subject.match(myregexp).length-1]次迭代。尽管如此,我会将你的答案标记为正确,因为这个正则表达式真的很棒。非常感谢你的努力。 - McFarlane
嗯,我可能错了,但似乎你不支持在令牌中出现“\”。例如,由“:”分隔的值可以是转义代码:“\n:\b:\c”,甚至是字符“a:b:,:;:\::”,这是一个包含“a”,“b”,“,”,“;”,“\”,“:”的列表。即使OP没有明确指定,如果不支持斜杠转义,你的解决方案感觉也不完整。而且到目前为止看起来你也不支持它。 - vaab

3
这里是一个解决方案:
function tokenize(str) {
  var reg = /((\\.|[^\\:])*)/g;
  var array = [];
  while(reg.lastIndex < str.length) {
    match = reg.exec(str);
    array.push(match[0].replace(/\\(\\|:)/g, "$1"));
    reg.lastIndex++;
  }
  return array;
}

它根据:字符将字符串分成令牌。

  • 但是,如果您想要:字符成为令牌的一部分,可以使用\进行转义。
  • 如果您想要\成为令牌的一部分,可以使用\进行转义。
  • 任何其他\都不会被解释。(例如:\a仍然是\a
  • 因此,只要在提前正确格式化数据,就可以将任何数据放入您的令牌中。

以下是一个示例,其中包含字符串\a:b:\n::\\:\::x,应该生成这些令牌:\ab\n<empty string>\:x

>>> tokenize("\\a:b:\\n::\\\\:\\::x");
["\a", "b", "\n", "", "\", ":", "x"]

为了更加清晰:放入分词器的字符串将被解释,其中有两个特殊字符:\:
  • \只有在后面跟着\:才有特殊意义,并且会有效地“转义”这些字符:意味着它们将失去分词器的特殊含义,并被视为任何普通字符(因此它们将成为标记的一部分)。
  • :是分隔两个标记的标记。
我意识到OP没有要求斜杠转义,但其他观众可能需要一个完整的解析库来允许数据中的任何字符。

2

使用负向回顾后发断言。

(.*?)((?<!\\):|$)

这将仅匹配:,如果它没有\之前出现。

它在我的正则表达式测试器中可以工作,但在我的代码中却无法工作。我正在使用JavaScript作为编程语言。尽管我找不到任何定义它们的页面,但它似乎有一些限制。 - McFarlane
3
JavaScript 不支持向后断言。 - Tim Pietzcker
好的,那么可以使用字符类来替代,例如 ([^\\]:|$) - Karl Barker
嗯,我猜可能还会出现引号斜杠“\”。因此,你的正则表达式将避免错误地匹配“\:”字符串,但其中确实存在一个有效的标记。 - vaab

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接