Espressione regolare per rimuovere un parametro dalla stringa di query

Sto cercando un’espressione regolare per rimuovere un singolo parametro da una stringa di query e, se ansible, voglio farlo in una singola espressione regolare.

Dire che voglio rimuovere il parametro foo . In questo momento lo uso:

 /&?foo\=[^&]+/ 

Questo funziona fintanto che foo non è il primo parametro nella stringa di query. Se lo è, la mia nuova stringa di query inizia con una e commerciale. (Ad esempio, ” foo=123&bar=456 ” dà un risultato di ” &bar=456 “.) In questo momento, sto solo controllando dopo la regex se la stringa di query inizia con la e commerciale e se la interrompe se lo fa.

Casi di bordo di esempio:

 Input | Expected Output -------------------------+-------------------- foo=123 | (empty string) foo=123&bar=456 | bar=456 bar=456&foo=123 | bar=456 abc=789&foo=123&bar=456 | abc=789&bar=456 

modificare

OK, come sottolineato nei commenti, ci sono molti più casi limite rispetto a quelli originariamente considerati. Ho ottenuto la regex seguente per lavorare con tutti loro:

 /&foo(\=[^&]*)?(?=&|$)|^foo(\=[^&]*)?(&|$)/ 

Questo è stato modificato dalla risposta di Mark Byers , ed è per questo che lo accetto, ma l’input di Roger Pate ha aiutato molto.

Ecco la suite completa di casi di test che sto utilizzando e uno snippet di Javascript che li testa:

 $(function() { var regex = /&foo(\=[^&]*)?(?=&|$)|^foo(\=[^&]*)?(&|$)/; var escapeHtml = function (str) { var map = { '&': '&', '': '>', '"': '"', "'": ''' }; return str.replace(/[&"']/g, function(m) { return map[m]; }); }; //test cases var tests = [ 'foo' , 'foo&bar=456' , 'bar=456&foo' , 'abc=789&foo&bar=456' ,'foo=' , 'foo=&bar=456' , 'bar=456&foo=' , 'abc=789&foo=&bar=456' ,'foo=123' , 'foo=123&bar=456' , 'bar=456&foo=123' , 'abc=789&foo=123&bar=456' ,'xfoo' , 'xfoo&bar=456' , 'bar=456&xfoo' , 'abc=789&xfoo&bar=456' ,'xfoo=' , 'xfoo=&bar=456' , 'bar=456&xfoo=' , 'abc=789&xfoo=&bar=456' ,'xfoo=123', 'xfoo=123&bar=456', 'bar=456&xfoo=123', 'abc=789&xfoo=123&bar=456' ,'foox' , 'foox&bar=456' , 'bar=456&foox' , 'abc=789&foox&bar=456' ,'foox=' , 'foox=&bar=456' , 'bar=456&foox=' , 'abc=789&foox=&bar=456' ,'foox=123', 'foox=123&bar=456', 'bar=456&foox=123', 'abc=789&foox=123&bar=456' ]; //expected results var expected = [ '' , 'bar=456' , 'bar=456' , 'abc=789&bar=456' ,'' , 'bar=456' , 'bar=456' , 'abc=789&bar=456' ,'' , 'bar=456' , 'bar=456' , 'abc=789&bar=456' ,'xfoo' , 'xfoo&bar=456' , 'bar=456&xfoo' , 'abc=789&xfoo&bar=456' ,'xfoo=' , 'xfoo=&bar=456' , 'bar=456&xfoo=' , 'abc=789&xfoo=&bar=456' ,'xfoo=123', 'xfoo=123&bar=456', 'bar=456&xfoo=123', 'abc=789&xfoo=123&bar=456' ,'foox' , 'foox&bar=456' , 'bar=456&foox' , 'abc=789&foox&bar=456' ,'foox=' , 'foox=&bar=456' , 'bar=456&foox=' , 'abc=789&foox=&bar=456' ,'foox=123', 'foox=123&bar=456', 'bar=456&foox=123', 'abc=789&foox=123&bar=456' ]; for(var i = 0; i < tests.length; i++) { var output = tests[i].replace(regex, ''); var success = (output == expected[i]); $('#output').append( '' + '' + (success ? 'PASS' : 'FAIL') + '' + '' + escapeHtml(tests[i]) + '' + '' + escapeHtml(output) + '' + '' + escapeHtml(expected[i]) + '' + '' ); } }); 
 #output { border-collapse: collapse; } #output tr.passed { background-color: #af8; } #output tr.failed { background-color: #fc8; } #output td, #output th { border: 1px solid black; padding: 2px; } 
  
Succ? Input Output Expected

Se vuoi farlo con una sola espressione regolare, puoi farlo:

 /&foo(=[^&]*)?|^foo(=[^&]*)?&?/ 

Questo perché devi abbinare una o più commerciale prima di foo = …, o uno dopo, o nessuno dei due, ma non entrambi.

Per essere onesti, penso che sia meglio il modo in cui l’hai fatto: rimuovere la trailer finale e in un passaggio separato.

 /(?<=&|\?)foo(=[^&]*)?(&|$)/ 

Usa lookbehind e l'ultimo gruppo per "ancorare" la partita e consente un valore mancante. Cambia \? a ^ se hai già rimosso il punto interrogativo dalla stringa di query.

Tuttavia, Regex non è ancora un sostituto di un parser reale della stringa di query.

Aggiornamento: script di prova: ( eseguilo su codepad.org )

 import re regex = r"(^|(?<=&))foo(=[^&]*)?(&|$)" cases = { "foo=123": "", "foo=123&bar=456": "bar=456", "bar=456&foo=123": "bar=456", "abc=789&foo=123&bar=456": "abc=789&bar=456", "oopsfoo=123": "oopsfoo=123", "oopsfoo=123&bar=456": "oopsfoo=123&bar=456", "bar=456&oopsfoo=123": "bar=456&oopsfoo=123", "abc=789&oopsfoo=123&bar=456": "abc=789&oopsfoo=123&bar=456", "foo": "", "foo&bar=456": "bar=456", "bar=456&foo": "bar=456", "abc=789&foo&bar=456": "abc=789&bar=456", "foo=": "", "foo=&bar=456": "bar=456", "bar=456&foo=": "bar=456", "abc=789&foo=&bar=456": "abc=789&bar=456", } failures = 0 for input, expected in cases.items(): got = re.sub(regex, "", input) if got != expected: print "failed: input=%r expected=%r got=%r" % (input, expected, got) failures += 1 if not failures: print "Success" 

Mostra dove il mio approccio è fallito, Mark ha il diritto di farlo - che dovrebbe mostrare perché non dovresti farlo con espressioni regolari ..: P


Il problema è associare il parametro di query con una sola e commerciale e, se è necessario utilizzare regex (se non lo si è verificato: P, utilizzerei un parser separato, che potrebbe usare regex al suo interno, ma in realtà capire il formato) -una soluzione sarebbe assicurarsi che ci sia esattamente una e commerciale per parametro: sostituire il leader ? con un & .

Questo dà /&foo(=[^&]*)?(?=&|$)/ , Che è molto semplice e il meglio che otterrai. Rimuovi l'iniziale & il risultato finale (o cambialo in un ? Ecc.). La modifica del caso di test per fare ciò usa gli stessi casi come sopra, e cambia il ciclo in:

 failures = 0 for input, expected in cases.items(): input = "&" + input got = re.sub(regex, "", input) if got[:1] == "&": got = got[1:] if got != expected: print "failed: input=%r expected=%r got=%r" % (input, expected, got) failures += 1 if not failures: print "Success" 

Avere una stringa di query che inizia con & è innocua – perché non lasciarla così? In ogni caso, ti suggerisco di cercare la trailer finale e utilizzare \b per far corrispondere l’inizio di foo w / o prendere in un personaggio precedente:

  /\bfoo\=[^&]+&?/ 

È un po ‘sciocco, ma ho iniziato a provare a risolverlo con una regexp e volevo finalmente farlo funzionare 🙂

 $str[] = 'foo=123'; $str[] = 'foo=123&bar=456'; $str[] = 'bar=456&foo=123'; $str[] = 'abc=789&foo=123&bar=456'; foreach ($str as $string) { echo preg_replace('#(?:^|\b)(&?)foo=[^&]+(&?)#e', "'$1'=='&' && '$2'=='&' ? '&' : ''", $string), "\n"; } 

la parte di sostituzione è incasinata perché apparentemente si confonde se i caratteri catturati sono '&' s

Inoltre, non corrisponde afoo e simili.

Grazie. Sì, utilizza i backslash per l’escape, e hai ragione, non ho bisogno di / s.

Questo sembra funzionare, anche se non lo fa in una riga come richiesto nella domanda originale.

  public static string RemoveQueryStringParameter(string url, string keyToRemove) { //if first parameter, leave ?, take away trailing & string pattern = @"\?" + keyToRemove + "[^&]*&?"; url = Regex.Replace(url, pattern, "?"); //if subsequent parameter, take away leading & pattern = "&" + keyToRemove + "[^&]*"; url = Regex.Replace(url, pattern, ""); return url; } 

Mi sono basato sulla tua implementazione per ottenere un impl Java che sembra funzionare:

  public static String removeParameterFromQueryString(String queryString,String paramToRemove) { Preconditions.checkArgument(queryString != null,"Empty querystring"); Preconditions.checkArgument(paramToRemove != null,"Empty param"); String oneParam = "^"+paramToRemove+"(=[^&]*)$"; String begin = "^"+paramToRemove+"(=[^&]*)(&?)"; String end = "&"+paramToRemove+"(=[^&]*)$"; String middle = "(?<=[&])"+paramToRemove+"(=[^&]*)&"; String removedMiddleParams = queryString.replaceAll(middle,""); String removedBeginParams = removedMiddleParams.replaceAll(begin,""); String removedEndParams = removedBeginParams.replaceAll(end,""); return removedEndParams.replaceAll(oneParam,""); } 

In alcuni casi ho avuto problemi con la tua implementazione perché a volte non eliminava un & e lo facevo con più passaggi che sembravano più facili da capire.

Ho avuto un problema con la tua versione, in particolare quando un parametro era nella stringa di query più volte (come param1 = toto & param2 = xxx & param1 = YYY e param3 = ZZZ & param1 ....)

Puoi usare la seguente espressione regolare:

 [\?|&](?.*?)=[^&]*&? 

Se vuoi fare una corrispondenza esatta, puoi sostituire (?.*?) Con un parametro url. per esempio:

 [\?|&]foo=[^&]*&? 

per abbinare qualsiasi variabile come foo=xxxx in qualsiasi URL.

Per chiunque sia interessato a sostituire i parametri di richiesta GET:

La regex seguente funziona anche per query di metodo GET più generali (a partire da?) In cui la risposta contrassegnata non riesce se il parametro da rimuovere è il primo (dopo?)

Questa regex (JS flavor) può essere utilizzata per rimuovere il parametro indipendentemente dalla posizione (prima, ultima o intermedia) lasciando la query in uno stato ben formato.

Quindi basta usare una regex replace con una stringa vuota.

 /&s=[^&]*()|\?s=[^&]*$|s=[^&]*&/ 

Fondamentalmente corrisponde a uno dei tre casi menzionati sopra (da qui i 2 tubi)