【问题标题】:Split string on spaces and quotes, keeping quoted substrings intact在空格和引号上拆分字符串,保持带引号的子字符串不变
【发布时间】:2018-03-08 18:00:22
【问题描述】:

我需要一种在空格上拆分字符串但保持带引号的子字符串完整的方法。

例如:

Input:
str = 'this "is a"test string'

Output:
[this, is a, test, string]

当我使用时:

str.match(/\\?.|^$/g).reduce((p, c) => {
    if(c === '"' || 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

它保持引用的子字符串完整,并根据需要在空格上拆分。

但是,在提供的示例中,它不会正确拆分术语,其中带引号的子字符串后面紧跟一个字母。相反,我得到的结果是这样的:

[this, is atest, string]

编辑:

我相信这个问题与其他类似的问题不同,因为它们都没有排除引号 并且 在结束引号后没有空格时正确拆分术语,就像在这种情况下:'this "is a"test string' .

【问题讨论】:

标签: javascript regex


【解决方案1】:

很多关于 SO 的类似“分割空格和引号”问答,其中大部分使用正则表达式解决方案。事实上,您的代码至少可以在one of them 中找到(感谢for thattry-catch-finally)。

虽然其中一些解决方案不包括引号,但如果右引号后面没有空格分隔符,我能找到的只有一个有效,并且它们都没有排除引号并允许缺少空格。

这也不仅仅是调整任何正则表达式的简单问题。如果您确实将正则表达式更改为使用捕获组,那么简单的match 方法将不再可行。 (通常的技术是在循环中使用exec。)如果您不使用捕获组,则需要在之后进行字符串操作以删除引号。

最简洁的解决方案是在来自match 的数组结果上使用map

使用slice 字符串操作方法:

var str = 'this "is a"test string';
var result = str.match(/"[^"]*"|\S+/g).map(m => m.slice(0, 1) === '"'? m.slice(1, -1): m);
console.log(result);

使用捕获组:

var str = 'this "is a"test string';
var regex = /"([^"]*)"|(\S+)/g;
var result = (str.match(regex) || []).map(m => m.replace(regex, '$1$2'));
console.log(result);

捕获组解决方案是一种更通用的解决方案,例如,可轻松扩展以允许使用不同的引号。

请注意,上述两种解决方案中使用的正则表达式非常简单,仅适用于双引号,子字符串中没有转义引号。 (不过,它适用于嵌套的单引号和撇号。)

正则表达式的解释:

  • "[^"]*"" 后跟任意数量的非" 字符后跟"

  • | → 或

  • \S+ → 任意连续的非空白字符序列

请注意,两组的顺序很关键。如果首先使用\S+,它将匹配开头的引号和后面的第一个单词。


至于您尝试使用的状态机代码,它的限制非常严格,仅适用于精确术语之间的一个空格,如果在任何地方使用任何撇号,则中断(因为它还允许子字符串被单引号)。

可以通过在检测到结束引号时推送一个空字符串来修复您的特定示例。为了在结束引号后也允许有一个空格,需要在推送新字符串之前检查现有的空字符串:

var str = 'this "is a"test string';
var result = str.match(/\\?.|^$/g).reduce((p, c) => {
    if(c === '"' || c === "'"){
        if(!(p.quote ^= 1)){p.a.push('');} // <- modified
    }else if(!p.quote && c === ' ' && p.a[p.a.length-1] !== ''){ // <- modified
        p.a.push('');
    }else{
        p.a[p.a.length-1] += c.replace(/\\(.)/,"$1");
    }
    return  p;
}, {a: ['']}).a
console.log(result);

【讨论】:

    猜你喜欢
    • 2023-01-13
    • 2011-04-16
    • 2010-09-09
    • 2013-02-17
    • 2016-11-08
    • 1970-01-01
    相关资源
    最近更新 更多