Регулярное выражение не соответствует пустой строке в многострочном режиме (Java)

Я только что наблюдал это поведение;

Pattern p1 = Pattern.compile("^$");
Matcher m1 = p1.matcher("");
System.out.println(m1.matches()); /* true */

Pattern p2 = Pattern.compile("^$", Pattern.MULTILINE);
Matcher m2 = p2.matcher("");
System.out.println(m2.matches()); /* false */

Мне кажется странным, что последнее утверждение ложно. Это то, что говорят документы;

По умолчанию регулярные выражения ^ и $игнорируют линейные терминаторы и соответствуют только в начале и в конце всей входной последовательности. Если режим MULTILINE активирован, то ^ совпадает в начале ввода и после любого терминатора линии, кроме как в конце ввода. Когда в режиме MULTILINE $совпадает только перед терминатором линии или концом входной последовательности. http://docs.oracle.com/javase/1.4.2...

Из чего я получаю от этого, он должен соответствовать? Следующее делает вещи еще более запутанными;

Pattern p3 = Pattern.compile("^test$");
Matcher m3 = p3.matcher("test");
System.out.println(m3.matches()); /* true */

Pattern p4 = Pattern.compile("^test$", Pattern.MULTILINE);
Matcher m4 = p4.matcher("test");
System.out.println(m4.matches()); /* true */

Так что это? Как я могу это понять? Я надеюсь, что кто-то может пролить свет на это, будет очень благодарен.

Ответы

Ответ 1

Если режим MULTILINE активирован, то ^ совпадает в начале вход и после любого терминатора линии, кроме как в конце ввода.

Поскольку вы находитесь в конце ввода, ^ не может совпадать в многострочном режиме.

Это удивительно, даже отвратительно, но, тем не менее, согласно его документации.

Ответ 2

Давайте посмотрим немного ближе к вашему второму примеру:

Pattern p2 = Pattern.compile("^$", Pattern.MULTILINE);
Matcher m2 = p2.matcher("");
System.out.println(m2.matches()); /* false */

Итак, у вас есть строка в m2, которая пуста OR, содержит только символ конечной линии и других символов. Поэтому шаблон, чтобы соответствовать данной строке, должен быть только "$" i.e.:

// Your example
Pattern p2 = Pattern.compile("^$", Pattern.MULTILINE);
Matcher m2 = p2.matcher("");
System.out.println(m2.matches()); /* false */

// Let check if it is start of the line
p2 = Pattern.compile("^", Pattern.MULTILINE);
m2 = p2.matcher("");
System.out.println(m2.matches()); /* false */

// Let check if it is end of the line
p2 = Pattern.compile("$", Pattern.MULTILINE);
m2 = p2.matcher("");
System.out.println(m2.matches()); /* true */

Ответ 3

Звучит как ошибка. Максимум, в многострочном режиме, "^" и "$" можно интерпретировать как совпадение на внутренней границе линии. Возможно, Java не имеет расширенной структуры переменных состояний, как это делает Perl. Я не знаю, является ли это даже причиной.

Тот факт, что /^test$/m соответствует только доказать, что функция $$ работает в многострочном режиме, за исключением случаев, когда строка пуста (на Java), но явно многострочный тест для пустой строки является смехотворным, поскольку /^$/ работает для этого.

Тестирование в Perl, все работает так, как ожидалось:

if ( "" =~ /^$/m   ) { print "/^\$/m    matches\n"; }
if ( "" =~ /^$/    ) { print "/^\$/     matches\n"; }
if ( "" =~ /\A\Z/m ) { print "/\\A\\Z/m  matches\n"; }
if ( "" =~ /\A\Z/  ) { print "/\\A\\Z/   matches\n"; }
if ( "" =~ /\A\z/  ) { print "/\\A\\z/   matches\n"; }
if ( "" =~ /^/m    ) { print "/^/m     matches\n"; }
if ( "" =~ /$/m    ) { print "/\$/m     matches\n"; }


__END__


/^$/m    matches
/^$/     matches
/\A\Z/m  matches
/\A\Z/   matches
/\A\z/   matches
/^/m     matches
/$/m     matches