我需要编写一个StringUtils.commaDelimitedListToStringArray函数的扩展版本,它获取一个额外的参数:escape char。
所以打电话给我:
commaDelimitedListToStringArray("test,test\\,test\\,test,test", "\\")
应该返回:
["test", "test,test,test", "test"]
我目前的尝试是使用String.split()来使用正则表达式分割String:
String[] array = str.split("[^\\\\],");
但返回的数组是:
["tes", "test\,test\,tes", "test"]
有什么想法吗?
答案 0 :(得分:32)
正则表达式
[^\\],
表示“匹配不是反斜杠后跟逗号的字符” - 这就是t,
等模式匹配的原因,因为t
是一个不是反斜杠的字符。
我认为您需要使用某种negative lookbehind来捕获,
,其前面没有\
而没有捕获前面的字符,例如
(?<!\\),
(顺便说一句,请注意,我故意没有加倍反复使用反斜杠以使其更具可读性)
答案 1 :(得分:30)
尝试:
String array[] = str.split("(?<!\\\\),");
基本上这是用逗号分隔,除非逗号前面有两个反斜杠。这称为negative lookbehind zero-width assertion。
答案 2 :(得分:6)
为了将来参考,以下是我最终的完整方法:
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;
}
答案 3 :(得分:2)
正如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个反斜杠结尾的元素的列表。这远远不够......但为什么要限制?有没有更有效的方式(不贪婪的背后)?无效字符串呢?
我搜索了一段通用解决方案,然后我自己编写了这个东西......想法是按照与列表元素匹配的模式进行拆分(而不是匹配分隔符)。
我的回答并没有将转义字符作为参数。
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;
}
模式描述(未转义):
(?<=(^|,))
转发是字符串的开头或,
([^\\,]|\\,|\\\\)*
由\,
,\\
或字符组成的元素既不是\
也不是,
(?=(,|$))
是字符串的结尾或,
模式可能会简化。
即使使用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;
}
答案 4 :(得分:1)
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' ]