Имеются строки вида:
Set-Cookie: Name=Value; expires=Tue, 01 Jan 2018 00:00:00 GMT; domain=.site.com; path=/; httponly
Суть задачи - получить Имя\значение куки и дату истечения.
Пробую регулярку, но не получается захватить сразу несколько групп:
Set-Cookie:( ([^=\n]+)=([^;\n]+);){1,}
Если убираю часть выделенную жирным - захватывается только первый параметр(Name=Value
), если оставляю - захватывается только последний параметр(path=/
).
Есть ли возможность вообще делать захват нескольких повторяющихся групп и если есть то как именно это верно записать?
ссылка на пример https://regex101.com/r/tUUOEW/1
p.s. Задачу решил иным способом, оставляю для истории и вроде вопрос может кому то помочь, не нашел как захватывать повторы групп(в отдельные совпадения). Буду благодарен за ответы по теме вопроса.
p.s.s. Для тех кто не заметил сам вопрос: Нужен способ находить "повторы групп" и делать их захват, обратите внимание на пример регулярного выражения, и его часть выделенную жирным.
В PHP нет возможности получить все подстроки, захваченные повторяющейся захватывающей группой/подмаской. В библиотеках .NET и regex
в Python есть такая возможность, но не в PHP.
Однако есть несколько путей решения.
Оператор \G
и множественные совпадения
Для того, чтобы извлечь несколько совпадений, следующих после какой-то определённой подстроки, можно воспользоваться оператором \G
(начало строки либо позиция сразу после найденного предыдущего совпадения). Это идеально подходит в случае, если такая "группа" одна (см. демо на PHP и демо регулярного выражения):
$s = "Set-Cookie: Name=Value; expires=Tue, 01 Jan 2018 00:00:00 GMT; domain=.site.com; path=/; httponly";
if (preg_match_all('~(?:\G(?!^);|Set-Cookie:)\s*\K([^=\n]+)(?:=([^;\n]+))?~', $s, $matches)) {
print_r(array_combine($matches[1],$matches[2]));
}
// Array
// (
// [Name] => Value
// [expires] => Tue, 01 Jan 2018 00:00:00 GMT
// [domain] => .site.com
// [path] => /
// [httponly] =>
// )
Подробности
(?:\G(?!^);|Set-Cookie:)
- подстрока Set-Cookie:
(начало поиска множественных (последовательных) совпадений или (|
) конец предыдущего совпадения (\G(?!^)
, (?!^)
необходим для того, чтобы "вычесть" из оператора \G
начало строки), за которым находится знак ;
\s*
- 0 и более пробельных символов\K
- оператор, удаляющий весь текст из текущего совпадения([^=\n]+)
- Подмаска №1 (ключ): 1 и более символов, отличных от знаков ;
и перевода строки(?:=([^;\n]+))?
- опциональная незахватывающая подмаска: 0 или 1 совпадение последовательности шаблонов:
=
- знак =
([^;\n]+)
- Подмаска №2 (значение, оно опционально): 1 и более символов, отличных от знаков ;
и перевода строкиЧтобы сделать что-то подобное для нескольких совпадений, придётся дополнить PHP-код:
$s = "Set-Cookie: Name=Value; expires=Tue, 01 Jan 2018 00:00:00 GMT; domain=.site.com; path=/; httponly\nSet-Cookie: Name=Another_Value; expires=Wed, 02 Jan 2019 00:00:00 GMT; domain=.another_site.com; path=/newpath; httponly";
$res = array();
if (preg_match_all('~(?:\G(?!^);|(Set-Cookie:))\s*\K([^=\n]+)(?:=([^;\n]+))?~', $s, $matches, PREG_SET_ORDER, 0)) {
foreach ($matches as $x) {
if (isset($x[1]) && strlen($x[1])) { // Начало нового совпадения
if (isset($tmp) && count($tmp) > 0) {
$res[] = $tmp;
}
$tmp = array();
}
$tmp[$x[2]] = isset($x[3]) && strlen($x[3]) ? $x[3] : "";
}
if (isset($tmp) && count($tmp) > 0) {
$res[] = $tmp;
}
}
print_r($res);
См. ещё одно демо на PHP. Заметьте, что захватывающих подмасок уже 3, а первая из них вокруг Set-Cookie:
техническая, благодаря ей можно узнать, когда начинается новое совпадение.
Решение в два прохода
Можно находить все куки, а потом из каждой получить только интересующую вас информацию.
Используйте что-то вроде этого выражения для нахождения всех куки:
Set-Cookie:((?: *[^=\n]+(?:=[^;\n]+)?)*)
Потом найдите все ключи-значения с помощью такого выражения в тексте, захваченном подмаской №1:
(\w+)(?:=([^\s;][^;\n]*))?
См. PHP-код:
$s = "Set-Cookie: Name=Value; expires=Tue, 01 Jan 2018 00:00:00 GMT; domain=.site.com; path=/; httponly\nSet-Cookie: Name=Another_Value; expires=Wed, 02 Jan 2019 00:00:00 GMT; domain=.another_site.com; path=/newpath; httponly";
$res = array();
if (preg_match_all('~Set-Cookie:((?:\h*[^=\n]+(?:=[^;\n]+)?)*)~', $s, $matches, PREG_SET_ORDER, 0)) {
foreach ($matches as $x) {
$tmp = preg_match_all('~(\w+)(?:=([^\s;][^;\n]*))?~', $x[1], $sub);
if ($tmp) {
$res[] = array_combine($sub[1], $sub[2]);
}
}
}
print_r($res);
Вы немного неправильно понимаете как работает регулярка. Регулярка сопоставляет символы. Вы пытаетесь найти вхождение Set-Cookie:
и дальше пачка групп k=v;
и регулярка ищет такие вхождения, но при этом вы пытаетесь захватывать строки, но ведь вхождение вашего выражения всего одно на строку! Вы захватите только один результат.
Чтобы сделать то что вы хотите, вы должны искать вхождения пар k=v;
. Вот что получается: (?:Set-Cookie:)?( ([^=\n]+)=([^;\n]+);)
. То есть, все пары, в том числе и те, перед которыми может быть стоит Set-Cookie:
.
Виртуальный выделенный сервер (VDS) становится отличным выбором
Верстаю в Sublime Text 3Был плагин, который показывал функции PHP, а название плагина вспомнить не могу
Хочу сделать простенький чатПримерно должно работать так:
Пропустил изучение регулярных выражений, а в задаче нужно сделать на php проверку, чтобы человек мог ввести время и минуты, максимум 99 часов...