Регулярное выражение, которое совпадает между кавычками, содержащими экранированные кавычки

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

В 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

Попробуйте следующее:

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

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