Поскольку Java поддерживает переменной длины просмотровых зады (до тех пор, как они конечны), вы могли бы сделать, это сделать так:
import java.util.regex.*;
public class RegexTest {
public static void main(String[] argv) {
Pattern p = Pattern.compile("(?<=(?<!\\\\)(?:\\\\\\\\){0,10}):");
String text = "foo:bar\\:baz\\\\:qux\\\\\\:quux\\\\\\\\:corge";
String[] parts = p.split(text);
System.out.printf("Input string: %s\n", text);
for (int i = 0; i < parts.length; i++) {
System.out.printf("Part %d: %s\n", i+1, parts[i]);
}
}
}
(?<=(?<!\\)(?:\\\\){0,10})
выглядит сзади для четное количество обратных косых черт (включая ноль, максимум до 10).
Выход:
Input string: foo:bar\:baz\\:qux\\\:quux\\\\:corge
Part 1: foo
Part 2: bar\:baz\\
Part 3: qux\\\:quux\\\\
Part 4: corge
Другим способом будет соответствовать тем частям себя, вместо разделения на разделителях.
Pattern p2 = Pattern.compile("(?<=\\A|\\G:)((?:\\\\.|[^:\\\\])*)");
List<String> parts2 = new LinkedList<String>();
Matcher m = p2.matcher(text);
while (m.find()) {
parts2.add(m.group(1));
}
Странный синтаксис вытекает из того, что ему необходимо обрабатывать случай пустых предметов в начале и конце строки. Когда совпадение содержит ровно нулевые символы, следующая попытка запустит один символ за его конец. Если бы это не так, это соответствовало бы другой пустой строке, а другая, ad infinitum & hellip;
(?<=\A|\G:)
будет выглядеть позади либо для начала строки (первая часть), или в конце предыдущего матча, после чего сепаратора. Если бы мы сделали (?:\A|\G:)
, это не получится, если первая часть пуста (ввод начинается с разделителя).
\\.
соответствует любому экранированному персонажу.
[^:\\]
соответствует любому символу, который не находится в escape-последовательности (потому что \\.
уничтожил оба из них).
((?:\\.|[^:\\])*)
захватывает все символы до первого неэкранированного разделителя в группу захвата 1.
См. Также http://stackoverflow.com/questions/820172/how-to-split-a-comma-separated-string-while-ignoring-escaped-commas. –