JavaScript在char上拆分但忽略了双重转义字符

时间:2017-07-16 17:57:47

标签: javascript

我正在尝试做类似的事情,但无法让它发挥作用。

How to split a comma separated String while ignoring escaped commas?

我试图解决这个问题,但似乎无法做到这一点。

我想将字符串拆分为:,而不是转义的字符\\: (我的逃避字符是双斜线)

给出:dtet:du\\,eduh ei\\:di:e,j
预期结果:["dtet"] ["du\\,eduh ei\\:di][e,j"]

正则表达式链接: https://regex101.com/r/12j6er/1/

3 个答案:

答案 0 :(得分:2)

这是一个有点冗长的方法。但适合你。 JavaScript正则表达式不支持lookbehinds。但是你可以通过简单地反转原始字符串并使用lookahead分割字符串来实现。然后反转数组和其中的所有字符串,你就会得到你的结果。



function reverse(s) {
  var o = '';
  for (var i = s.length - 1; i >= 0; i--)
    o += s[i];
  return o;
}


var str = "dtet:du\\,eduh ei\\:di:e,j";
var res = reverse(str);
var result  = res.split(/:(?!\\)/g);
result  = result.reverse();
for(var i = 0; i < result.length; i++){
	result[i] = reverse(result[i]);
}

console.log(result);
&#13;
&#13;
&#13;

答案 1 :(得分:2)

我可以拿出两个解决方案。一个基于调整数组内容和一个使用正则表达式。

解决方案1:

方法:拆分:,然后将元素铲入新阵列并将这些元素粘合在一起,应该分裂。

function splitcolon(input) {
    var inparts = input.split(":");
    var outparts = [];
    var splitwaslegit = true;
    inparts.forEach(function(part) {
        if (splitwaslegit) {
            outparts.push(part);
        } else { // the split was not justified, so reverse it by gluing this part to the previous one
            outparts[outparts.length-1] += ":" + part;
        }
        // the next split was legit if this part doesn't end on \\
        splitwaslegit = (part.substring(part.length-2) !== "\\\\");
    });
    return outparts;
}

在chrome中测试:

splitcolon("dtet:du\\\\,eduh ei\\\\:di:e,j")
(3) ["dtet", "du\\,eduh ei\\:di", "e,j"]

注意:
当然也可以使用for循环或下划线each代替forEach

解决方案2:

方法:如果有任何char或字符串,你可以100%确定它不会在输入中,那么你可以使用该字符串/字符串作为临时分隔符插入由这样的正则表达式:

var tmpdelim = "\x00"; // must *never* ever occur in input string

var input = "dtet:du\\\\,eduh ei\\\\:di:e,j";
input.replace(/(^.?|[^\\].|.[^\\]):/g, "$1" + tmpdelim).split(tmpdelim);

结果:

(3) ["dtet", "du\\,eduh ei\\:di", "e,j"]

正则表达式/(^.?|[^\\].|.[^\\]):/g的解释:

/ - 正则表达式的开始
( - 匹配组1 开始
^.? - 我们在输入开始时或任何单个字符远离它(逃避需要2)
| - 或
[^\\]. - 任何不是\的字符,后跟任何其他字符 | - 或
.[^\\] - 除了\之外的任何其他字符 ) - 匹配组1 停止
: - 匹配组(不能\\)必须后跟:
/ - 正则表达式结束 g - 正则表达式修饰符全局(匹配所有出现,而不仅仅是第一个)

我们用$1 + tmpdelim替换,所以使用匹配组1 中的任何内容,然后是我们的特殊分隔符(而不是:),我们可以将其用于分割

奖金解决方案

Manjo Verma的答案是单线:

input.split("").reverse().join("").split(/:(?!\\\\)/).reverse().map(x => x.split("").reverse().join(""));

结果:

(3) ["dtet", "du\\,eduh ei\\:di", "e,j"]

答案 2 :(得分:1)

请参阅下面名为splitOnNonEscapedDelimeter()的函数,该函数接受要分割的string和要分割的delimeter,在本例中为:。用法在函数onChange()内。

  

请注意,您必须将传递给delimeter的{​​{1}}转义为splitOnNonEscapedDelimeter(),以免将其解释为special character in the regular expression

&#13;
&#13;
function nonEscapedDelimeter(delimeter) {
  return new RegExp(String.raw`[^${delimeter}]*?(?:\\\\${delimeter}[^${delimeter}]*?)*(?:${delimeter}|$)`, 'g')
}

function nonEscapedDelimeterAtEnd(delimeter) {
  return new RegExp(String.raw`([^\\].|.[^\\]|^.?)${delimeter}$`)
}

function splitOnNonEscapedDelimeter(string, delimeter) {
  const reMatch = nonEscapedDelimeter(delimeter)
  const reReplace = nonEscapedDelimeterAtEnd(delimeter)

  return string.match(reMatch).slice(0, -1).map(section => {
    return section.replace(reReplace, '$1')
  })
}

function onChange() {
  console.log(splitOnNonEscapedDelimeter(i.value, ':'))
}

i.addEventListener('change', onChange)

onChange()
&#13;
<textarea id=i>dtet:du\\,eduh ei\\:di:e,j</textarea>
&#13;
&#13;
&#13;

要求

此解决方案使用了ES2015功能String.raw()template literals以方便使用,但这些并非必需。如果您的目标平台不包含对这些功能的支持,请参阅上面的相关文档以了解其工作原理并使用a polyfill such as this

解释

new RegExp(String.raw`[^${delimeter}]*?(?:\\\\${delimeter}[^${delimeter}]*?)*(?:${delimeter}|$)`, 'g')

函数nonEscapedDelimeter()创建一个几乎完成所需操作的正则表达式,除了一些需要通过一些后处理纠正的怪癖之外。

string.match(reMatch)

正则表达式在String#match()中使用时,会将字符串拆分为以非转义delimeter结尾或字符串结尾的部分。这也有在字符串末尾匹配0宽度部分的副作用,因此我们需要

.slice(0, -1)

在后期处理中删除该匹配。

new RegExp(String.raw`([^\\].|.[^\\]|^.?)${delimeter}$`)

...

.map(section => {
  return section.replace(reReplace, '')
})

由于每个部分现在以delimeter结尾,但最后一部分(在字符串末尾结束),我们需要.map()匹配数组并删除非-escaped delimeter(因此nonEscapedDelimeterAtEnd()如此复杂),如果它在那里。