Поскольку 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.