【问题标题】:Split a string by whitespace, keeping quoted segments, allowing escaped quotes用空格分割字符串,保留带引号的段,允许转义引号
【发布时间】:2010-10-27 09:54:37
【问题描述】:

我目前有这个正则表达式可以用所有空格来分割字符串,除非它在带引号的段中:

keywords = 'pop rock "hard rock"';
keywords = keywords.match(/\w+|"[^"]+"/g);
console.log(keywords); // [pop, rock, "hard rock"]

但是,我也希望可以在关键字中使用引号,如下所示:

keywords = 'pop rock "hard rock" "\"dream\" pop"';

这应该返回

[pop, rock, "hard rock", "\"dream\" pop"]

实现这一目标的最简单方法是什么?

【问题讨论】:

    标签: javascript regex


    【解决方案1】:

    您可以将您的正则表达式更改为:

    keywords = keywords.match(/\w+|"(?:\\"|[^"])+"/g);
    

    你有 (?:\\"|[^"])+ 而不是 [^"]+,它允许 \" 或其他字符,但不能使用未转义的引号。

    一个重要的注意事项是,如果您希望字符串包含文字斜线,则应该是:

    keywords = 'pop rock "hard rock" "\\"dream\\" pop"'; //note the escaped slashes.
    

    此外,\w+[^"]+ 之间存在细微的不一致 - 例如,它将匹配单词 "ab*d",但不匹配 ab*d(不带引号)。考虑改用[^"\s]+,这将匹配非空格。

    【讨论】:

    • 我建议你使用\\.而不是\\",因为反斜杠也可以被转义,你不会想错过"foo\\\\"
    • @Tim - 一开始很有趣,但我不确定是否有必要 - [^"] 不会处理这些情况吗?我错过了什么吗?
    • 考虑这一点:在字符串 "\\" "foo" 中(为了清楚起见,只有两个反斜杠),第一个 " 将与正则表达式开头的文字 " 匹配。然后[^"] 将匹配第一个\。然后剩余的\" 将与\\" 匹配(因为它在交替中排在第一位)。然后[^"] 将匹配空格,"(在正则表达式的末尾)将匹配"foo" 的开头引号,从而中断解析。
    • 它可以正常工作。 "(?:\\"|[^"])+ 这应该是不言自明的"
    • @Kobi Fair 点。对于它所关心的人,我已将(?<!\\)(?:\\\\)* 添加到正则表达式中。也就是说,not 前面有一个反斜杠,然后必须有偶数个反斜杠(即转义的反斜杠)。换句话说,开引号必须以 0, 2, 4, 6, ... 反斜杠开头,否则(即 1, 3, ... 反斜杠)我们将不认为它是开引号。跨度>
    【解决方案2】:

    ES6 方案支持:

    • 除内引号外按空格分隔
    • 删除引号,但不删除反斜杠转义引号
    • 转义的引号变成引号
    • 可以在任何地方加上引号

    代码:

    keywords.match(/\\?.|^$/g).reduce((p, c) => {
            if(c === '"'){
                p.quote ^= 1;
            }else if(!p.quote && c === ' '){
                p.a.push('');
            }else{
                p.a[p.a.length-1] += c.replace(/\\(.)/,"$1");
            }
            return  p;
        }, {a: ['']}).a
    

    输出:

    [ 'pop', 'rock', 'hard rock', '"dream" pop' ]
    

    【讨论】:

    • 您的代码真的很难阅读,但它确实有效,正是我想要的。
    • 这行得通,并修剪了我需要的报价;同意虽然很难阅读
    【解决方案3】:

    如果 Kobi 的答案适用于示例字符串,那么当 Tim Pietzcker 在 cmets 中注意到它时引号之间有多个连续的转义字符 (反斜杠) 时,它就不行了。为了处理这些情况,模式可以这样写(用于匹配方法)

    (?=\S)[^"\s]*(?:"[^\\"]*(?:\\[\s\S][^\\"]*)*"[^"\s]*)*
    

    demo

    (?=\S) 确保在当前位置至少有一个非空白字符,因为以下描述了所有允许的子字符串 (包括引号之间的空格) 是完全可选的。

    详情:

    (?=\S)   # followed by a non-whitespace
    [^"\s]*  #"# zero or more characters that aren't a quote or a whitespace
    (?: # when a quoted substring occurs:
        "       #"# opening quote
        [^\\"]* #"# zero or more characters that aren't a quote or a backslash
        (?: # when a backslash is encountered:
            \\ [\s\S] # an escaped character (including a quote or a backslash)
            [^\\"]* #"#
        )*
        "         #"# closing quote
        [^"\s]*   #"#
    )*
    

    【讨论】:

    • 你能用简单的英文把每个部分的意图加上吗?
    • @Timo:我添加了模式细节。
    • 这行得通,并保留引用,因为我相信 OP 要求。
    • 这很好用!但是如果我想让它也适用于单引号怎么办?
    【解决方案4】:

    我想指出我和你有相同的正则表达式,

    /\w+|"[^"]+"/g
    

    但它不适用于空引号字符串,例如:

    "" "hello" "" "hi"
    

    所以我不得不将 + 量词更改为 *。 这给了我:

    str.match(/\w+|"[^"]*"/g);
    

    这很好。

    (例如:https://regex101.com/r/wm5puK/1

    【讨论】:

      猜你喜欢
      • 2010-09-09
      • 2013-12-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-26
      相关资源
      最近更新 更多