Ошибка компиляции подробного Java-регулярного выражения с символьным классом и границей слов
Почему этот шаблон не скомпилирован:
Pattern.compile("(?x)[ ]\\b");
ошибка
ERROR java.util.regex.PatternSyntaxException:
Illegal/unsupported escape sequence near index 8
(?x)[ ]\b
^
at java_util_regex_Pattern$compile.call (Unknown Source)
В то время как работают следующие эквивалентные?
Pattern.compile("(?x)\\ \\b");
Pattern.compile("[ ]\\b");
Pattern.compile(" \\b");
Это ошибка в компиляторе Java regex, или я чего-то не хватает? Мне нравится использовать [ ]
в расширенном регулярном выражении вместо обратного слэша-обратного слэша, потому что он сохраняет некоторый визуальный шум. Но, видимо, они не то же самое!
PS: этот вопрос не касается обратных косых черт. Это об экранировании пробелов в многословном регулярном выражении с использованием символьного класса, содержащего единственное пространство [ ]
вместо использования обратного слэша.
Так или иначе комбинация verbose regex (?x)
и класса символов, содержащего одно пространство [ ]
выдает компилятор и не распознает escape-последовательность слова \b
Протестировано с Java до 1.8.0_151
Ответы
Ответ 1
Это ошибка в peekPastWhitespace()
Java peekPastWhitespace()
в классе Pattern
. Отслеживание всей этой проблемы... Я решил взглянуть на реализацию OpenJDK 8-b132 Pattern
. Позвольте начать забивать это сверху:
-
compile()
вызывает expr()
в строке 1696 -
expr()
вызывает sequence()
в строке 1996 -
sequence()
вызывает clazz()
в строке 2063, так как случай [
был встречен -
clazz()
вызывает peek()
в строке 2509 -
peek()
вызывает peekPastWhitespace()
в строке 1830, так как if(has(COMMENTS))
оценивает значение true
(из-за того, что добавлен флаг x
(?x)
в начале шаблона) -
peekPastWhitespace()
(опубликовано ниже) пропускает все пробелы в шаблоне.
peekPastWhitespace()
private int peekPastWhitespace(int ch) {
while (ASCII.isSpace(ch) || ch == '#') {
while (ASCII.isSpace(ch))
ch = temp[++cursor]
if (ch == '#') {
ch = peekPastLine();
}
}
return ch;
}
Такая же ошибка существует в parsePastWhitespace()
.
Ваше регулярное выражение интерпретируется как []\\b
, что является причиной вашей ошибки, потому что \b
не поддерживается в классе символов в Java. Более того, как только вы исправляете проблему \b
, ваш класс символов также не имеет закрытия ]
.
Что вы можете сделать, чтобы исправить эту проблему:
-
\\
Как упоминалось в ОП, просто используйте двойную обратную косую черту и пространство -
[\\ ]
Побег в пространстве символов, чтобы он интерпретировался буквально -
[ ](?x)\\b
Поместите встроенный модификатор после класса символов
Ответ 2
Мне нравится использовать [ ]
в расширенном регулярном выражении вместо обратного слэша-обратного слэша, потому что он сохраняет некоторый визуальный шум. Но, видимо, они не то же самое!
"[ ]"
- это то же самое, что "\\ "
или даже " "
.
Проблема заключается в том, что (?x)
в начале включает режим комментариев. Как указано в документации
Разрешает пробелы и комментарии в шаблоне.
В этом режиме пробелы игнорируются, а встроенные комментарии, начинающиеся с #
, игнорируются до конца строки.
Режим комментариев также можно включить с помощью встроенного флага (?x)
.
В режиме комментариев регулярное выражение "(?x)[ ]\\b"
совпадает с "[]\\b"
и не будет компилироваться, потому что пустой класс символа []
не анализируется как пустой, а анализируется как "[\\]"
(незакрытый класс символов, содержащий литерал ]
).
Вместо этого используйте " \\b"
. Альтернативно, сохраните пространство в режиме комментариев, экранировав его обратным слэшем: "(?x)[\\ ]\\b"
или "(?x)\\ \\b"
.
Ответ 3
Похоже, из-за свободного пробега (verbose) mode (?x)
в [ ]
игнорируется, поэтому механизм regex видит ваше регулярное выражение как []\\b
.
Если мы удалим \\b
это будет выглядеть как []
и мы получим ошибку об Unclosed character class
- character class не может быть пустым, поэтому ]
помещены непосредственно после того, как [
рассматривается как первый символ, который принадлежит этому классу вместо метасимвола который закрывает класс символов.
Так как [
открыто, движок регулярных выражений видит \b
как помещенный внутри этого символьного класса. Но \b
не может быть размещен там (это не символ, а "место"), поэтому мы видим ошибку в "неподдерживаемой escape-последовательности" (внутри класса символов, но эта часть была пропущена).
Другими словами, вы не можете использовать [ ]
для выхода из пространства в подробном режиме (по крайней мере, на Java). Вам нужно будет либо использовать "\\ "
либо "[\\ ]"
.
Ответ 4
Обходной путь
Помимо ускорения пробелов отдельно, которые в буквальном смысле такие же, как [ ]
, вы можете использовать режим x
для всего регулярного выражения, но отключите его во время работы с шаблонами, которым требуются пробелы, inline:
(?x)match-this-(?-x: with spaces )\\b
^^^^^^^^^^^ ^^^^^^^^^^^^^ ^^^
'x' is on off on
или альтернативой будет использование метасимволов qouting \Q...\E
:
(?x)match-this-\Q with s p a c e s \E\\b
^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^ ^^^
'x' is on off on
Почему Exception
?
В расширенном или в режиме комментариев (x
) пробелы игнорируются, но обработка пробелов внутри классов символов в разных вариантах выполняется по-разному.
Например, в PCRE все пробельные символы игнорируются, за исключением тех, что указаны в символьном классе. Это означает, что [ ]
является допустимым регулярным выражением, но у Java нет исключения:
В этом режиме пробелы игнорируются...
Период. Таким образом, этот [ ]
равен этому []
который недопустим и PatternSyntaxException
исключение PatternSyntaxException
.
Почти все ароматы регулярных выражений, кроме JavaScript, нуждаются в классе символов, чтобы иметь хотя бы один блок данных. Они рассматривают пустой класс символов как незамкнутый набор, которому нужна закрывающая скобка. Сказать, что []]
действует в большинстве вкусов.
Режим свободного интервала в дефференциальных ароматах на [ ]
:
-
PCRE
действителен -
.NET
valid -
Perl
действует -
Ruby
valid -
TCL
действует - Ошибка
Java 7
- Ошибка
Java 8
Ответ 5
Давайте проанализируем, что произойдет точно.
Взгляните на исходный код java.util.regex.Pattern
Разрешает пробелы и комментарии в шаблоне. В этом режиме пробелы игнорируются, а встроенные комментарии, начинающиеся С#, игнорируются до конца строки.
Режим комментариев также можно включить с помощью встроенного флага (? X).
Ваше регулярное выражение направит вас к этой строке
private void accept(int ch, String s) {
int testChar = temp[cursor++];
if (has(COMMENTS))
testChar = parsePastWhitespace(testChar);
if (ch != testChar) {
throw error(s);
}
}
Если вы заметили свой кодовый вызов parsePastWhitespace (testChar);
private int parsePastWhitespace(int ch) {
while (ASCII.isSpace(ch) || ch == '#') {
while (ASCII.isSpace(ch))//<----------------Here is the key of your error
ch = temp[cursor++];
if (ch == '#')
ch = parsePastLine();
}
return ch;
}
В вашем случае у вас есть пробел в вашем регулярном выражении (?x)[ ]\\b
это вернет что-то (я не могу проанализировать его правильно):
if (ch != testChar) {
throw error(s);
}
который не равен ch
и здесь исключение составляет броски
throw error(s);