2009-03-29 17 views
10

Questa era in origine una domanda che volevo porre, ma mentre ricercavo i dettagli per la domanda, ho trovato la soluzione e ho pensato che potesse interessare agli altri.Espressione regolare che corrisponde tra virgolette, contenente citazioni di escape

In Apache, la richiesta di piena è tra virgolette e le eventuali citazioni all'interno sono sempre cavata con un backslash:

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

Sto cercando di costruire un'espressione regolare che corrisponde a tutti i campi distinti. La mia soluzione attuale si ferma sempre al primo preventivo dopo la GET/POST (in realtà ho solo bisogno di tutti i valori tra cui la dimensione trasferito):

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

Credo che andrò anche fornire la mia soluzione dalla mia sorgente PHP con i commenti e la formattazione meglio:

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

Utilizzando questo con un semplice caso in cui l'URL non contiene altre citazioni funziona bene:

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

Ora sto cercando di ottenere supporto per nessuna, una o più occorrenze di \", ma non riesco a trovare una soluzione. Utilizzando regexpal.com Ho arrivato fino a questo finora:

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

Ecco solo la parte modificata:

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

Tuttavia, è troppo avido. Mangia tutto fino all'ultimo ", quando dovrebbe mangiare solo fino al primo " non preceduto da un \. Ho anche provato introducendo il requisito che non c'è \ prima della " che voglio, ma mangia ancora alla fine della stringa (Nota: ho dovuto aggiungere estranei \ personaggi per fare questo lavoro in PHP):

# request uri 
    '"(.|\\(?="))*[^\\\\]"' . 
* ?::? Se utilizzato subito dopo una delle quantificatori , +, o {}, rende il quantificatore non avido (corrispondente al numero minimo di volte)

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

Ma poi mi ha colpito

La regex completa:

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

aggiornamento 5 maggio 2009:

ho scoperto un piccolo difetto nella espressione regolare a causa di analisi milioni di righe: si rompe sulle linee che contengono il carattere di backslash a destra prima del doppio citazione. In altre parole:

...\\" 

interromperà l'espressione regolare. Apache non registra ...\" ma eviterà sempre la barra retroversa su \\, quindi è lecito ritenere che quando ci sono due caratteri di barra rovesciata prima della virgoletta doppia.

Qualcuno ha un'idea su come risolvere questo problema con l'espressione regolare?

risorse utili: the JavaScript Regexp documentation at developer.mozilla.org e regexpal.com

risposta

26

Prova questo:

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

Questa espressione regolare corrisponde a un carattere doppio apice seguito da una sequenza di una qualsiasi carattere diverso da \ e " o una sequenza sfuggito \α (dove α può essere qualsiasi carattere) f seguito dall'ultimo carattere di doppia citazione. Il (?:expr) sintassi è solo un gruppo non catturante.

+2

Potresti aggiungere ulteriori informazioni sulla tua espressione regolare a beneficio di tutti? Sono riuscito a malapena a capire quello che ho scritto ... grazie :) – mark

+5

(?: A | B) corrisponde a A o B. \\. corrisponde a una barra rovesciata che segue qualsiasi carattere tranne newline. [^ \\ "] corrisponde a qualsiasi carattere tranne il backslash e il doppio preventivo. Mettere tutto insieme fa esattamente quello che vuoi, +1. –

+0

bellissimo ... grazie amico. –

Problemi correlati