Почему конечное повторение во взглядах не работает в некоторых вариантах? - PullRequest
3 голосов
/ 01 июля 2010

Я хочу проанализировать 2 цифры в середине от даты в формате dd/mm/yy, но также разрешить использование одной цифры для дня и месяца.

Вот что я придумал:

(?<=^[\d]{1,2}\/)[\d]{1,2}

Мне нужен 1 или 2-значный номер [\d]{1,2} с 1 или 2-значным номером и косой чертой ^[\d]{1,2}\/ перед ним.

Это не работает для многих комбинаций, я проверял 10/10/10, 11/12/13 и т. Д. *

Но к моему удивлению (?<=^\d\d\/)[\d]{1,2} сработало.

Но [\d]{1,2} также должен совпадать, если \d\d сделал, или я ошибаюсь?

Ответы [ 4 ]

13 голосов
/ 01 июля 2010

Поддержка поддержки lookbehind

Основные разновидности regex имеют различные поддержки для lookbehind по-разному;некоторые налагают определенные ограничения, а некоторые даже не поддерживают его вообще.

  • Javascript: не поддерживается
  • Python: только фиксированная длина
  • Java: конечная длинатолько
  • .NET: без ограничений

Ссылки


Python

В Python, где поддерживается только задний просмотр фиксированной длины, ваш оригинальный шаблон вызывает ошибку, поскольку \d{1,2} явно не имеет фиксированной длины.Вы можете «исправить» это, чередуя два разных вида сзади фиксированной длины, например, что-то вроде этого:

(?<=^\d\/)\d{1,2}|(?<=^\d\d\/)\d{1,2}

Или, возможно, вы можете поместить оба вида взгляда как альтернативы не захватывающей группы:

(?:(?<=^\d\/)|(?<=^\d\d\/))\d{1,2}

(обратите внимание, что вы можете просто использовать \d без скобок).

Тем не менее, вероятно, вместо этого гораздо проще использовать группу захвата:

^\d{1,2}\/(\d{1,2})

Примечаниечто findall возвращает то, что захватывает группа 1, если у вас есть только одна группа.Группа захвата поддерживается более широко, чем внешний вид, и часто приводит к более читаемому шаблону (например, в этом случае).

Этот фрагмент иллюстрирует все вышеперечисленные пункты:

p = re.compile(r'(?:(?<=^\d\/)|(?<=^\d\d\/))\d{1,2}')

print(p.findall("12/34/56"))   # "[34]"
print(p.findall("1/23/45"))    # "[23]"

p = re.compile(r'^\d{1,2}\/(\d{1,2})')

print(p.findall("12/34/56"))   # "[34]"
print(p.findall("1/23/45"))    # "[23]"

p = re.compile(r'(?<=^\d{1,2}\/)\d{1,2}')
# raise error("look-behind requires fixed-width pattern")

Ссылки


Java

Java поддерживает только просмотр конечной длины, поэтому вы можете использовать \d{1,2}, как в исходном шаблоне.Это демонстрируется следующим фрагментом:

    String text =
        "12/34/56 date\n" +
        "1/23/45 another date\n";

    Pattern p = Pattern.compile("(?m)(?<=^\\d{1,2}/)\\d{1,2}");
    Matcher m = p.matcher(text);
    while (m.find()) {
        System.out.println(m.group());
    } // "34", "23"

Обратите внимание, что (?m) является встроенным Pattern.MULTILINE, так что ^ соответствует началу каждой строки.Также обратите внимание, что, поскольку \ является escape-символом для строковых литералов, вы должны написать "\\", чтобы получить одну обратную косую черту в Java.


C-Sharp

C # поддерживает полное регулярное выражениена взгляд позади.В следующем фрагменте показано, как можно использовать + повторение для внешнего вида:

var text = @"
1/23/45
12/34/56
123/45/67
1234/56/78
";

Regex r = new Regex(@"(?m)(?<=^\d+/)\d{1,2}");
foreach (Match m in r.Matches(text)) {
  Console.WriteLine(m);
} // "23", "34", "45", "56"

Обратите внимание, что в отличие от Java, в C # вы можете использовать @ - заключенную в кавычки строку , чтобы вы нене нужно бежать \.

Для полноты, вот как вы можете использовать опцию захвата группы в C #:

Regex r = new Regex(@"(?m)^\d+/(\d{1,2})");
foreach (Match m in r.Matches(text)) {
  Console.WriteLine("Matched [" + m + "]; month = " + m.Groups[1]);
}

Учитывая предыдущий text, это печатает:

Matched [1/23]; month = 23
Matched [12/34]; month = 34
Matched [123/45]; month = 45
Matched [1234/56]; month = 56

Смежные вопросы

4 голосов
/ 01 июля 2010

Если нет особой причины для использования внешнего вида, который не отмечен в вопросе, как насчет простого сопоставления всего этого и захвата только интересующего вас бита?

Пример JavaScript:

>>> /^\d{1,2}\/(\d{1,2})\/\d{1,2}$/.exec("12/12/12")[1]
"12"
3 голосов
/ 01 июля 2010

Цитировать регулярные выражения.инфо :

Плохая новость в том, что большинство регулярных выражений ароматы не позволяют использовать только любое регулярное выражение внутри взгляда, потому что они не могут применять регулярные выражения в обратном направлении. Поэтому регулярный двигатель выражения должен быть в состоянии выяснить, сколько шагов сделать шаг назад перед проверкой вида сзади.

Поэтому многие вкусы регулярных выражений, в том числе те, которые используются Perl и Python, разрешить только фиксированной длины строки. Вы можете использовать любое регулярное выражение какая длина матча может быть предопределено. Это означает, что вы можете использовать буквальный текст и классы символов. Вы не можете использовать повторение или необязательно Предметы. Вы можете использовать чередование, но только если все варианты в чередовании имеют одинаковую длину.

Другими словами, ваше регулярное выражение не работает, потому что вы используете выражение переменной ширины внутри просмотра, а ваш механизм регулярных выражений не поддерживает это.

2 голосов
/ 05 июля 2010

В дополнение к тем, которые перечислены @polygenelubricants, есть еще два исключения из правила «только фиксированная длина».В PCRE (движок регулярных выражений для PHP, Apache, и др. ) и Oniguruma (Ruby 1.9, Textmate) просмотр назад может состоять из чередования, в котором каждая альтернатива может соответствовать разному количеству символов, еслипоскольку длина каждой альтернативы фиксирована.Например:

(?<=\b\d\d/|\b\d/)\d{1,2}(?=/\d{2}\b)

Обратите внимание, что чередование должно быть на верхнем уровне подвыражения lookbehind.У вас, как и у меня, может возникнуть соблазн выделить общие элементы, например:

(?<=\b(?:\d\d/|\d)/)\d{1,2}(?=/\d{2}\b)

... но это не сработает;на верхнем уровне подвыражение теперь состоит из единственной альтернативы с нефиксированной длиной.

Второе исключение гораздо полезнее: \K, поддерживается Perl и PCRE.Это фактически означает «притвориться, что матч действительно начался здесь».Все, что предстает перед этим в регулярном выражении, рассматривается как позитивный взгляд назад.Как и в случае .NET, нет никаких ограничений;все, что может появиться в обычном регулярном выражении, можно использовать до \K.

\b\d{1,2}/\K\d{1,2}(?=/\d{2}\b)

Но большую часть времени, когда у кого-то возникают проблемы со взглядами, оказывается, что им даже не следует их использовать,Как отметил @insin, эту проблему можно решить гораздо проще, используя группу захвата.

РЕДАКТИРОВАТЬ: Почти забыл JGSoft, разновидность регулярных выражений, используемая EditPad Pro и PowerGrep;как и .NET, он имеет абсолютно неограниченный вид, положительный и отрицательный.

...