Регулярное выражение, совпадающее между кавычками, содержащее экранированные кавычки - PullRequest
10 голосов
/ 29 марта 2009

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

В Apache полный запрос заключен в двойные кавычки, а любые кавычки внутри всегда экранируются обратной косой чертой:

1.2.3.4 - - [15/Apr/2005:20:35:37 +0200] "GET /\" foo=bat\" HTTP/1.0" 400 299 "-" "-" "-"

Я пытаюсь создать регулярное выражение, которое соответствует всем различным полям. Мое текущее решение всегда останавливается на первой кавычке после GET / POST (на самом деле мне нужны только все значения, включая переданный размер):

^(\d+\.\d+\.\d+\.\d+)\s+[^\s]+\s+[^\s]+\s+\[(\d+)/([A-Za-z]+)/(\d+):(\d+):(\d+):(\d+)\s+\+\d+\]\s+"[^"]+"\s+(\d+)\s+(\d+|-)

Полагаю, я также предоставлю свое решение из моего источника PHP с комментариями и улучшенным форматированием:

$sPattern = ';^' .
    # ip address: 1
    '(\d+\.\d+\.\d+\.\d+)' .
    # ident and user id
    '\s+[^\s]+\s+[^\s]+\s+' .
    # 2 day/3 month/4 year:5 hh:6 mm:7 ss +timezone
    '\[(\d+)/([A-Za-z]+)/(\d+):(\d+):(\d+):(\d+)\s+\+\d+\]' .
    # whitespace
    '\s+' .
    # request uri
    '"[^"]+"' .
    # whitespace
    '\s+' .
    # 8 status code
    '(\d+)' .
    # whitespace
    '\s+' .
    # 9 bytes sent
    '(\d+|-)' .
    # end of regex
    ';';

Использование этого в простом случае, когда URL не содержит других кавычек, работает нормально:

1.2.3.4 - - [15/Apr/2005:20:35:37 +0200] "GET /\ foo=bat\ HTTP/1.0" 400 299 "-" "-" "-"

Теперь я пытаюсь получить поддержку ни одного, одного или нескольких вхождений \", но не могу найти решение. Используя regexpal.com, я до сих пор придумал это:

^(\d+\.\d+\.\d+\.\d+)\s+[^\s]+\s+[^\s]+\s+\[(\d+)/([A-Za-z]+)/(\d+):(\d+):(\d+):(\d+)\s+\+\d+\]\s+"(.|\\(?="))*"

Вот только измененная часть:

    # request uri
    '"(.|\\(?="))*"' .

Однако, это слишком жадный. Он ест все до последнего ", когда он должен есть только до первого ", которому не предшествует \. Я также попытался ввести требование о том, что \ не нужно до ", которое я хочу, но оно все же съедает до конца строки (Примечание: мне пришлось добавить посторонние символы \, чтобы это работало в PHP):

    # request uri
    '"(.|\\(?="))*[^\\\\]"' .

Но затем меня поразило: *?: При использовании сразу после любого из квантификаторов , +,? Или {} делает квантификатор нежадным (соответствует минимуму количество раз)

    # request uri
    '"(.|\\(?="))*?[^\\\\]"' .

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

^(\d+\.\d+\.\d+\.\d+)\s+[^\s]+\s+[^\s]+\s+\[(\d+)/([A-Za-z]+)/(\d+):(\d+):(\d+):(\d+)\s+\+\d+\]\s+"(.|\\(?="))*?[^\\]"\s+(\d+)\s+(\d+|-)

Обновление от 5 мая 2009 г .:

Я обнаружил небольшой недостаток в регулярном выражении из-за разбора миллионов строк: он разбивается на строки, содержащие символ обратной косой черты прямо перед двойной кавычкой. Другими словами:

...\\"

сломает регулярное выражение. Apache не регистрирует ...\", но всегда переводит обратную косую черту в \\, поэтому можно с уверенностью предположить, что, если перед двойной кавычкой есть два символа обратной косой черты.

У кого-нибудь есть идеи, как это исправить с помощью регулярного выражения?

Полезные ресурсы: документация JavaScript Regexp на developer.mozilla.org и regexpal.com

1 Ответ

26 голосов
/ 29 марта 2009

Попробуйте это:

"(?:[^\\"]+|\\.)*"

Это регулярное выражение соответствует символу двойной кавычки, за которым следует последовательность любого символа, кроме \ и ", или экранированная последовательность \α (где α может быть любым символом), за которым следует последний символ двойной кавычки. Синтаксис (?:expr) является просто не захватывающей группой.

...