Подтвердить что ты не робот

В чем разница между \z и\Z в регулярном выражении и когда и как его использовать?

Из http://java.sun.com/j2se/1.5.0/docs/api/java/util/regex/Pattern.html:

\Z  The end of the input but for the final terminator, if any
\z  The end of the input

Но что это означает на практике? Можете ли вы привести мне пример, когда я использую либо \Z, либо\z.

В моем тесте я думал, что "StackOverflow\n".matches("StackOverflow\\z") вернет true, а "StackOverflow\n".matches("StackOverflow\\z") вернет false. Но на самом деле оба возвращают ложь. Где ошибка?

4b9b3361

Ответ 1

  Хотя \Z и $ совпадают только в конце строки (когда вариант для каретки и доллара, чтобы соответствовать во встроенных переносах строки выключено), есть одно исключение. Если строка заканчивается разрывом строки, тогда \Z и $ будут совпадать в позиции до разрыва строки, а не в самом конце строки.

Это "улучшение" было введено Perl и скопировано многими регулярными выражениями вкусы, включая Java,.NET и PCRE. В Perl при чтении строки из файла результирующая строка оканчивается переводом строки. чтение строка из файла с текстом "joe" приводит к строке joe\n. Применительно к этой строке, оба ^[a-z]+$ и \A[a-z]+\Z будут соответствует "Джо".

Если вы хотите найти совпадение только в самом конце строки, используйте \z (строчная z вместо прописной Z). \A[a-z]+\z не соответствовать Джо \n. \z соответствует после разрыва строки, который не соответствует по классу персонажей.

http://www.regular-expressions.info/anchors.html

То, как я читаю это "Qaru\n".matches("Qaru\\z"), должно возвращать false, потому что ваш шаблон не включает перевод строки.

"Qaru\n".matches("Qaru\\z\\n") => false
"Qaru\n".matches("Qaru\\Z\\n") => true

Ответ 2

Просто проверил. Похоже, когда вызывается Matcher.matches() (как в вашем коде, за кулисами),\Z ведет себя как \z. Однако, когда вызывается Matcher.find(), они ведут себя по-другому, как ожидалось. Следующее возвращает true:

Pattern p = Pattern.compile("StackOverflow\\Z");
Matcher m = p.matcher("StackOverflow\n");
System.out.println(m.find());

и если вы замените \Z на\z, он вернет false.

Я нахожу это немного удивительным...

Ответ 3

Я думаю, что главная проблема здесь заключается в неожиданном поведении matches(): любое совпадение должно потреблять всю строку ввода. Оба из ваших примеров не работают, потому что регулярные выражения не потребляют строку в конце строки. Якорь не имеет к этому никакого отношения.

В большинстве языков совпадение регулярных выражений может происходить где угодно, потребляя все, некоторые или ни одну из входных строк. И Java имеет метод Matcher#find(), который выполняет этот традиционный вид соответствия. Однако результаты противоположны тому, что вы сказали, что ожидаете:

Pattern.compile("StackOverflow\\z").matcher("StackOverflow\n").find()  //false
Pattern.compile("StackOverflow\\Z").matcher("StackOverflow\n").find()  //true

В первом примере \z должен соответствовать концу строки, но трейлинг-строка в пути. Во втором случае \z совпадает с параметром linefeed, который находится в конце строки.

Ответ 4

Как сказал Эяль, он работает для find(), но не для совпадений().

Это действительно имеет смысл. Сам якорь \Z фактически соответствует позиции прямо перед окончательным терминатором eol, но регулярное выражение в целом не соответствует, потому что в целом ему нужно сопоставить весь текст, который соответствует, и ничто не соответствует терминатору. (\ Z соответствует позиции прямо перед терминатором, что не то же самое.)

Если вы сделали "StackOverflow\n".matches("StackOverflow\\Z.*"), вы должны быть в порядке.

Ответ 5

\Z совпадает с $, он соответствует концу строки, конец строки может сопровождаться разрывом строки.

введите описание изображения здесь введите описание изображения здесь

\Z соответствует концу строки, не может следовать разрыву строки.

введите описание изображения здесь введите описание изображения здесь

Ответ 6

Я думаю, что Алан Мур дал лучший ответ, особенно критический момент, который matches молча вставляет ^ и $ в свои аргументы регулярного выражения.

Я также хотел бы добавить несколько примеров. И немного больше объяснений.

\z соответствует только в самом конце строки.

\Z также совпадает в самом конце строки, но если есть \n, оно будет соответствовать перед ним.

Рассмотрим эту программу:

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Main {
    public static void main(String[] args) {
        Pattern p = Pattern.compile(".+\\Z"); // some word before the end of the string
        String text = "one\ntwo\nthree\nfour\n";
        Matcher m = p.matcher(text);
        while (m.find()) {
            System.out.println(m.group());
        }
    }
}

Он найдет 1 совпадение и напечатает "four".

Измените \Z на \z, и он не будет ничего соответствовать, потому что он не хочет совпадать до \n.

Однако это также напечатает four, потому что в конце нет \n:

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Main {
    public static void main(String[] args) {
        Pattern p = Pattern.compile(".+\\z");
        String text = "one\ntwo\nthree\nfour";
        Matcher m = p.matcher(text);
        while (m.find()) {
            System.out.println(m.group());
        }
    }
}