Регулярное выражение Cookie

128
04 июля 2019, 12:40

Имеются строки вида:

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. Для тех кто не заметил сам вопрос: Нужен способ находить "повторы групп" и делать их захват, обратите внимание на пример регулярного выражения, и его часть выделенную жирным.

Answer 1

В 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);
Answer 2

Вы немного неправильно понимаете как работает регулярка. Регулярка сопоставляет символы. Вы пытаетесь найти вхождение Set-Cookie: и дальше пачка групп k=v; и регулярка ищет такие вхождения, но при этом вы пытаетесь захватывать строки, но ведь вхождение вашего выражения всего одно на строку! Вы захватите только один результат.

Чтобы сделать то что вы хотите, вы должны искать вхождения пар k=v;. Вот что получается: (?:Set-Cookie:)?( ([^=\n]+)=([^;\n]+);). То есть, все пары, в том числе и те, перед которыми может быть стоит Set-Cookie:.

READ ALSO
Плагин Sublime Text

Плагин Sublime Text

Верстаю в Sublime Text 3Был плагин, который показывал функции PHP, а название плагина вспомнить не могу

132
Обновление div из php без перегрузки страницы

Обновление div из php без перегрузки страницы

Хочу сделать простенький чатПримерно должно работать так:

134
Регулярное выражения

Регулярное выражения

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

123
как соединить два файла?

как соединить два файла?

создал два файла gamecpp game

143