Expression régulière qui correspond à entre guillemets, contenant échappé citations

C'était à l'origine une question que je voulais poser, mais alors que des recherches sur les détails de la question, j'ai trouvé la solution et de la pensée, il peut être d'intérêt pour les autres.

Dans Apache, la demande complète est dans les guillemets doubles et les guillemets à l'intérieur sont toujours par un antislash:

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

Je suis en train de construire une expression régulière qui correspond à tous les domaines distincts. Ma solution actuelle s'arrête toujours sur le premier devis après le GET/POST (en fait j'ai seulement besoin de toutes les valeurs, y compris la taille transférés):

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

Je crois que je vais aussi donner mon solution de ma source en PHP avec des commentaires et une meilleure mise en forme:

$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
    ';';

À l'aide de ce avec un cas simple où l'URL ne contient pas d'autres citations fonctionne très bien:

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

Maintenant, je vais essayer d'obtenir un soutien pour aucune, une ou plusieurs occurrences de \" en elle, mais ne peut pas trouver une solution. À l'aide de regexpal.com je suis venu avec cette mesure:

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

Ici est seulement la modification de la partie:

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

Cependant, il est trop gourmand. Il mange de tout jusqu'à la dernière ", quand il ne doit manger jusqu'à ce que la première " pas précédée par une \. J'ai aussi essayé d'introduire l'exigence qu'il n'y a pas de \ avant la " je veux, mais encore, elle mange à la fin de la chaîne (Note: j'ai dû ajouter de superflu \ de caractères pour faire ce travail en PHP):

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

Mais ensuite il m'a frappé: *?: Si utilisé immédiatement après l'une des quantificateurs , +, ?, ou {}, rend le quantificateur non-greedy (correspondant au nombre de fois minimum)

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

La pleine regex:

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

Mise à jour le 5 Mai 2009:

J'ai découvert une petite faille dans la regexp en raison d'analyse des millions de lignes: il casse sur les lignes qui contiennent le caractère barre oblique inverse à droite avant le guillemet. En d'autres termes:

...\\"

va casser les regex. Apache va pas dans le journal ...\" mais toujours échapper à la barre oblique inverse à \\, de sorte qu'il est sûr de supposer que, lorsqu'il s'agit de deux caractères barre oblique inverse avant le guillemet.

Quelqu'un a une idée de comment résoudre ce problème avec les regex?

Ressources utiles: le JavaScript Regexp de la documentation à developer.mozilla.org et regexpal.com

OriginalL'auteur |