Первоначально это вопрос, который я хотел задать, но, исследуя детали для вопроса, я нашел решение и подумал, что это может представлять интерес для других.
В 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