Как сказал Мэтт 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 обратной косой черты. Это достаточно далеко ... но зачем предел? Есть ли более эффективный способ (не жадно смотреть за спиной)? А как насчет неверных строк?
Я какое-то время искал универсальное решение, затем сам написал это ... Идея состоит в том, чтобы разбить, следуя шаблону, который соответствует элементам списка (а не разделителю).
Мой ответ не принимает escape-символ в качестве параметра.
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. Его по-прежнему можно оптимизировать, написав определенный синтаксический анализатор.
Кроме того, зачем иметь escape-символ, если только один символ особенный, его можно просто удвоить ...
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;
}