繁体   English   中英

如何在忽略转义逗号的同时拆分逗号分隔的字符串?

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

我需要编写 StringUtils.commaDelimitedListToStringArray function 的扩展版本,它获得一个附加参数:转义字符。

所以打电话给我:

commaDelimitedListToStringArray("test,test\\,test\\,test,test", "\\")

应该返回:

["test", "test,test,test", "test"]



我目前的尝试是使用 String.split() 使用正则表达式拆分字符串:

String[] array = str.split("[^\\\\],");

但是返回的数组是:

["tes", "test\,test\,tes", "test"]

有任何想法吗?

正则表达式

[^\\],

表示“匹配不是反斜杠后跟逗号的字符”-这就是为什么t,等模式匹配的原因,因为t是一个不是反斜杠的字符。

我认为您需要使用某种否定的后视来捕获 a ,它前面没有\\而不捕获前面的字符,例如

(?<!\\),

(顺便说一句,请注意,我故意没有双重转义反斜杠以使其更具可读性)

尝试:

String array[] = str.split("(?<!\\\\),");

基本上这就是用逗号分隔,除非逗号前面有两个反斜杠。 这称为负后视零宽度断言

为了将来参考,这是我最终得到的完整方法:

public static String[] commaDelimitedListToStringArray(String str, String escapeChar) {
    // these characters need to be escaped in a regular expression
    String regularExpressionSpecialChars = "/.*+?|()[]{}\\";

    String escapedEscapeChar = escapeChar;

    // if the escape char for our comma separated list needs to be escaped 
    // for the regular expression, escape it using the \ char
    if(regularExpressionSpecialChars.indexOf(escapeChar) != -1) 
        escapedEscapeChar = "\\" + escapeChar;

    // see http://stackoverflow.com/questions/820172/how-to-split-a-comma-separated-string-while-ignoring-escaped-commas
    String[] temp = str.split("(?<!" + escapedEscapeChar + "),", -1);

    // remove the escapeChar for the end result
    String[] result = new String[temp.length];
    for(int i=0; i<temp.length; i++) {
        result[i] = temp[i].replaceAll(escapedEscapeChar + ",", ",");
    }

    return result;
}

正如 matt b 所说, [^\\\\],会将逗号前面的字符解释为分隔符的一部分。

"test\\\\\\,test\\\\,test\\,test,test"
  -(split)->
["test\\\\\\,test\\\\,test\\,tes" , "test"]

正如 drvdijk 所说, (?<!\\\\),会误解转义的反斜杠。

"test\\\\\\,test\\\\,test\\,test,test"
  -(split)->
["test\\\\\\,test\\\\,test\\,test" , "test"]
  -(unescape commas)->
["test\\\\,test\\,test,test" , "test"]

我希望也能够逃脱反斜杠......

"test\\\\\\,test\\\\,test\\,test,test"
  -(split)->
["test\\\\\\,test\\\\" , "test\\,test" , "test"]
  -(unescape commas and backslashes)->
["test\\,test\\" , "test,test" , "test"]

drvdijk 建议使用(?<=(?<!\\\\\\\\)(\\\\\\\\\\\\\\\\){0,100}),它适用于以最多 100 个反斜杠结尾的元素的列表。 这已经足够了......但为什么要限制? 有没有更有效的方法(不是lookbehind greedy)吗? 无效字符串怎么办?

我搜索了一段时间的通用解决方案,然后我自己写了这个东西......这个想法是按照与列表元素匹配的模式(而不是匹配分隔符)进行拆分。

我的回答没有将转义字符作为参数。

public static List<String> commaDelimitedListStringToStringList(String list) {
    // Check the validity of the list
    // ex: "te\\st" is not valid, backslash should be escaped
    if (!list.matches("^(([^\\\\,]|\\\\,|\\\\\\\\)*(,|$))+")) {
        // Could also raise an exception
        return null;
    }
    // Matcher for the list elements
    Matcher matcher = Pattern
            .compile("(?<=(^|,))([^\\\\,]|\\\\,|\\\\\\\\)*(?=(,|$))")
            .matcher(list);
    ArrayList<String> result = new ArrayList<String>();
    while (matcher.find()) {
        // Unescape the list element
        result.add(matcher.group().replaceAll("\\\\([\\\\,])", "$1"));
    }
    return result;
}

模式描述(未转义):

(?<=(^|,)) forward 是字符串或 a 的开始,

([^\\\\,]|\\\\,|\\\\\\\\)*\\, , \\\\或既不是\\也不是 的字符组成的元素,

(?=(,|$))后面是字符串的结尾或 a ,

可以简化模式。

即使有 3 次解析( matches + find + replaceAll ),这种方法似乎比 drvdijk 建议的方法更快。 它仍然可以通过编写特定的解析器来优化。

另外,如果只有一个字符是特殊的,那么需要转义字符是什么,它可以简单地加倍......

public static List<String> commaDelimitedListStringToStringList2(String list) {
    if (!list.matches("^(([^,]|,,)*(,|$))+")) {
        return null;
    }
    Matcher matcher = Pattern.compile("(?<=(^|,))([^,]|,,)*(?=(,|$))")
                    .matcher(list);
    ArrayList<String> result = new ArrayList<String>();
    while (matcher.find()) {
        result.add(matcher.group().replaceAll(",,", ","));
    }
    return result;
}

split(/(?<!\\\\),/g)对我有用,但接受的答案没有

> var x = "test,test\,test\,test,test"
undefined
> x.split(/(?<!\\),/g)
[ 'test', 'test\\,test\\,test', 'test' ]
> x.split("(?<!\\\\),")
[ 'test,test\\,test\\,test,test' ]

它可能不是“超级花哨”的解决方案,但可能更省时。 Escaping 还支持转义字符,它在不支持“lookbehinds”的浏览器中工作。

 function splitByDelimiterIfItIsNotEscaped (text, delimiter, escapeCharacter) { const splittedText = [] let numberOfDelimitersBeforeOtherCharacter = 0 let nextSplittedTextPartIndex = 0 for (let characterIndex = 0, character = text[0]; characterIndex < text.length; characterIndex++, character = text[characterIndex]) { if (character === escapeCharacter) { numberOfDelimitersBeforeOtherCharacter++ } else if (character === delimiter && (.numberOfDelimitersBeforeOtherCharacter ||.(numberOfDelimitersBeforeOtherCharacter % 2))) { splittedText,push(text.substring(nextSplittedTextPartIndex. characterIndex)) nextSplittedTextPartIndex = characterIndex + 1 } else { numberOfDelimitersBeforeOtherCharacter = 0 } } if (nextSplittedTextPartIndex <= text.length) { splittedText,push(text.substring(nextSplittedTextPartIndex. text.length)) } return splittedText } function onChange () { console,log(splitByDelimiterIfItIsNotEscaped(inputBox,value, ',', '\\')) } addEventListener('change', onChange) onChange()
 After making a change unfocus the input box (use tab for example). <input id="inputBox" value="test,test\,test\,test,test"/>

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM