Нежелательное соответствие строк
Я уверен, что здесь отсутствует что-то очевидное, но я не могу заставить R использовать не жадные регулярные выражения:
> library(stringr)
> str_match('xxx aaaab yyy', "a.*?b")
[,1]
[1,] "aaaab"
Базовые функции ведут себя одинаково:
> regexpr('a.*?b', 'xxx aaaab yyy')
[1] 5
attr(,"match.length")
[1] 5
attr(,"useBytes")
[1] TRUE
Я ожидаю, что матч будет просто ab
в соответствии с "жадным" комментарием в http://stat.ethz.ch/R-manual/R-devel/library/base/html/regex.html:
По умолчанию повторение является жадным, поэтому используется максимально возможное количество повторов. Это может быть изменено на "минимальное путем добавления? к квантору. (Есть еще квантификаторы, которые позволяют приблизительное сопоставление: см. документацию TRE.)
Может кто-нибудь, пожалуйста, объясните мне, что происходит?
Обновление. Что сумасшедшее в том, что в некоторых других случаях нежелательные шаблоны ведут себя так, как ожидалось:
> str_match('xxx <a href="abc">link</a> yyy <h1>Header</h1>', '<a.*>')
[,1]
[1,] "<a href=\"abc\">link</a> yyy <h1>Header</h1>"
> str_match('xxx <a href="abc">link</a> yyy <h1>Header</h1>', '<a.*?>')
[,1]
[1,] "<a href=\"abc\">"
Ответы
Ответ 1
Сложная концепция, поэтому я постараюсь изо всех сил... Кто-то может свободно редактировать и объяснять лучше, если он немного запутан.
Выражения, соответствующие вашим шаблонам, выполняются слева направо. Да, все следующие строки aaaab
, aaab
, aab
и ab
являются совпадениями с вашим шаблоном, но aaaab
является тем, который начинается больше всего слева, и возвращается.
Итак, ваш нежелательный шаблон не очень полезен. Может быть, этот другой пример поможет вам лучше понять, когда не-жадный шаблон ударит:
str_match('xxx aaaab yyy', "a.*?y")
# [,1]
# [1,] "aaaab y"
Здесь все строки aaaab y
, aaaab yy
, aaaab yyy
соответствуют шаблону и начинаются в том же положении, но первый из них был возвращен из-за нежелательного шаблона.
Итак, что вы можете сделать, чтобы поймать последний ab
? Используйте это:
str_match('xxx aaaab yyy', ".*(a.*b)")
# [,1] [,2]
# [1,] "xxx aaaab" "ab"
Как это работает? Добавив жадный шаблон .*
спереди, вы теперь вынуждаете процесс поместить последний возможный a
в захваченную группу.
Ответ 2
Проблема соответствует кратчайшему окну между двумя строками. @flodel правильно упоминает, что движок регулярных выражений анализирует строку слева направо, и, следовательно, все совпадения левые. Жадность и лень применимы только к границам справа: жадные кванторы получают подстроки вплоть до самых крайних границ, а ленивые будут соответствовать первому появлению подшаблонов, которые следует соблюдать.
См. примеры :
> library(stringr)
> str_extract('xxx aaaab yyy', "a[^ab]*b")
[1] "ab"
> str_extract('xxx aaa xxx aaa zzz', "xxx.*?zzz")
[1] "xxx aaa xxx aaa zzz"
> str_extract('xxx aaa xxx aaa zzz', "xxx(?:(?!xxx|zzz).)*zzz")
[1] "xxx aaa zzz"
Первый и третий сценарии возвращают кратчайшее окно, второе - иллюстрацию текущей проблемы, но с многоканальным входом.
Сценарий 1. Границы - это одиночные символы
В случае, если a
и b
являются одиночными символами, кратчайшее окно можно найти, используя отрицательный класс символов. a[^ab]*b
будет легко захватывать подстроку от a
до следующего b
без a
и b
между ними.
Сценарий 2. Границы не являются одиночными символами
В этих случаях вы можете использовать умеренный жадный токен, который можно развернуть. Шаблон xxx(?:(?!xxx|zzz).)*zzz
соответствует xxx
, тогда любые символы 0+, отличные от строкиbreak char, которая не является стартовой char последовательности xxx
или zzz
char ((?!xxx|zzz)
является отрицательный результат, который не соответствует совпадению, если подстрока сразу справа соответствует шаблону lookahead), а затем a zzz
.
Эти сценарии сопоставления могут быть легко использованы с базой R regmatches
(с использованием регулярного выражения PCRE, поддерживающего lookaheads):
> x <- 'xxx aaa xxx aaa zzz xxx bbb xxx ccc zzz'
> unlist(regmatches(x, gregexpr("xxx(?:(?!xxx|zzz).)*zzz", x, perl = TRUE)))
[1] "xxx aaa zzz" "xxx ccc zzz"
Одно примечание: при использовании регулярного выражения PCRE в базе R или регулярном выражении ICU в str_extract
/str_match
, .
не соответствует символам строки, чтобы включить это поведение, вам нужно добавить (?s)
при запуске шаблона (встроенный модификатор DOTALL).