【问题标题】:Split a string where the separators can be escaped拆分可以转义分隔符的字符串
【发布时间】:2018-04-07 23:07:46
【问题描述】:

这是一个非常简单的正则表达式,但我无法理解如何扩展此正则表达式,以便只要它在字符串中转义就可以使用分隔符。这是我所拥有的:

// Contents of str is exactly '|1|2|\|Three and Four\||5'
str.match(/[^|]/);

// Looking for: ['1', '2', '|Three and Four|', '5']

所以目前我的正则表达式选择了不是| 字符的所有内容,并且我得到了每个项目的数组。但我想要做的是忽略| 字符作为分隔符,如果它首先用\ 转义,但我当然不希望\ 通过。

我知道这将被标记为其他十亿个正则表达式问题的副本,但我尝试在此处将其他解决方案应用于我自己的解决方案,并使用 regex101.com。唉,我的Regex Fu不强。

附:有人知道学习 JS 风格的正则表达式的好资源吗?

【问题讨论】:

  • @charlietfl 该列中的| 被转义(\|),因此是 not 分隔符。考虑变体(转义的分隔符没有与正常的分隔符紧贴在一起):|1|hello\|happy\|world|2 -> '1', 'hello|happy|world', '2'
  • var str = '|1|2|\|Three and Four\||5';equals to var str = '|1|2||Three and Four||5'; in js
  • @xianshenglu @user2864740 没错,没想说清楚。这是一个数据流,我已将其转换为字符串,以便我可以对其进行操作并访问| 之间的每个项目,但其中一些项目包括不应被视为分隔符的|
  • RegEx needed to split javascript string on "|" but not "\|" 的可能重复项(在那里,找到了一些东西:D)
  • 注意:副本仍然留在(实际上要求这样)\| -- 这可以通过替换 \| 来纠正在拆分后的结果组件中。

标签: javascript regex split escaping


【解决方案1】:

应该这样做:

var str =  '|1|2|\\|Three and Four\\||5';
str.match(/((\\\|)|[^|])+/gi)

我的输出是这样的:

 ["1", "2", "\|Three and Four\|", "5"]

我所做的是在第一个子模式中创建一个匹配 \| 字符串的模式,然后匹配不是 | 的任何内容。我也转义了\,因为否则在javascript中编写该字符串只会将它们解析为转义字符。

【讨论】:

    【解决方案2】:

    如果将 JavaScript 与支持负向后视的正则表达式引擎(例如 Chrome)一起使用,并且在仅显示单个/简单转义且没有转义方法的情况下 - -escape,可以使用比较简单的负向look-behind:

    '|1|2|\\|Three and Four\\||5'.split(/(?<!\\)\|/)
    
    # -> ["", "1", "2", "\|Three and Four\|", "5"]
    

    这说明 - 在支持负面后视的 Chrome 中 - 在“|”上拆分前面没有“\”。

    Here is a method to convert a look-behind to a look-ahead 用于引擎兼容性。 RegEx needed to split javascript string on "|" but not "\|" 也讨论了变化。

    但是,正如所指出的,上述不涉及 \|序列,因此留在转义序列中。


    或者,多步骤方法也可以解决这个问题,它可以将转义字符作为过程的一部分。

    1. 用“替代”字符/字符串替换转义的分隔符
    2. 在剩余的(非转义的)分隔符上拆分
    3. 将“替代”字符/字符串转换回各个组件中

    在代码中,

    str = '|1|2|\\|Three and Four\\||5'
    
    # replace \| -> "alternative"
    # this assumes that \\| (escape-the-escape) is not allowed
    rep = str.replace(/\\[|]/g, '~~~~')
    
    # replace back, without any of the escapes
    res = rep.split('|').map(function (f) { return f.replace(/~~~~/g, "|") })
    
    # res -> ["", "1", "2", "|Three and Four|", "5"]
    

    【讨论】:

    • 谢谢。我想我可能对 \ 字符有点困惑,我对 JS 还很陌生。所以最后的字符串应该正好包含|Three and Four|,没有任何斜线。
    • @Jamie4840 啊,是的。这将需要对原始 \| 所示的拆分用法进行修饰。分隔符序列被简单地忽略。
    • 啊,明白了!明白了。
    • 替代解决方案仍然需要新分隔符的转义方法(~~~~),因此它基本上是将问题转移到新层而不是解决它
    【解决方案3】:

    Paul G Mihai's answer 工作正常,但不捕获空字符串:a||b|c 将返回 [ "a", "b", "c" ],而不是人们可能想要的 [ "a", "", "b", "c" ]

    根据他的解决方案进行详细说明,这是一种获取空字符串的方法,模仿 split() 的相同行为:

    str.match(
      /((\\\|)|[^\|])*/gi
    ).filter(
      (e, i, a) => !(i > 0 && e == "" && a[i-1] != "")
    )
    

    我在这里所做的是使用具有相同模式的match(),但允许零长度匹配(* 而不是+)。

    这为我提供了一个匹配数组,其中每个找到的分隔符和字符串末尾都有一个空字符串元素,例如:a|b|c 将返回 [ "a", "", "b", "", "c", "" ]

    然后我filter()它,丢弃非空字符串元素之后的任何空字符串元素,因此我摆脱了不需要的项目。

    这似乎也能正确处理边缘情况:

    a||b|c         → ["a", "", "b", "c"]
    a|b|||c        → ["a", "b", "", "", "c"]
    a|b\|b|c|      → ["a", "b\|b", "c", ""]
    |a|\|b\||c|    → ["", "a", "\|b\|", "c", ""]
    (empty string) → [""]
    

    【讨论】:

      最近更新 更多