Expresión regular para eliminar un parámetro de la cadena de consulta

Estoy buscando una expresión regular para eliminar un solo parámetro de una cadena de consulta, y quiero hacerlo en una sola expresión regular si es posible.

Digamos que quiero eliminar el parámetro foo . Ahora mismo uso esto:

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

Eso funciona siempre que foo no sea el primer parámetro en la cadena de consulta. Si es así, mi nueva cadena de consulta comienza con un signo de unión. (Por ejemplo, ” foo=123&bar=456 ” da un resultado de ” &bar=456 “.) En este momento, solo estoy verificando la expresión regular si la cadena de consulta comienza con ampersand y cortándolo si lo hace.

Ejemplos de casos extremos:

 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 

Editar

OK, como se señala en los comentarios, hay mucho más casos extremos de los que consideré originalmente. Obtuve la siguiente expresión regular para trabajar con todos ellos:

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

Esto se modificó a partir de la respuesta de Mark Byers , que es la razón por la que estoy aceptando esa, pero la contribución de Roger Pate también ayudó mucho.

Aquí está el conjunto completo de casos de prueba que estoy usando, y un fragmento de Javascript que los prueba:

 $(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

Si desea hacer esto en una sola expresión regular, puede hacer esto:

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

Esto se debe a que necesita un signo ampersand antes del foo = …, o uno después, o ninguno, pero no ambos.

Para ser sincero, creo que es mejor de la forma en que lo hiciste: eliminar el ampersand final en un paso separado.

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

Utiliza lookbehind y el último grupo para "anclar" la coincidencia y permite un valor faltante. Cambiar el \? a ^ si ya ha eliminado el signo de interrogación de la cadena de consulta.

Regex aún no es un sustituto de un analizador real de la cadena de consulta, sin embargo.

Actualización: script de prueba: (ejecutarlo en 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" 

Muestra dónde falló mi enfoque, Mark tiene el derecho, lo que debería mostrar por qué no debes hacer esto con expresiones regulares ...: P


El problema es asociar el parámetro de consulta con exactamente un signo comercial y, si debe usar la expresión regular (si no la ha elegido: P, usaría un analizador separado, que podría usar expresiones regulares dentro de él, pero aún así entender el formato): una solución sería asegurarse de que haya exactamente un signo comercial por parámetro: ¿reemplazar el líder ? con un & .

Esto le da a /&foo(=[^&]*)?(?=&|$)/ , Lo cual es muy directo y lo mejor que va a obtener. Elimine el resultado inicial & final (o cámbielo de nuevo a ? Etc.). La modificación del caso de prueba para hacer esto utiliza los mismos casos que antes, y cambia el ciclo a:

 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" 

Tener una cadena de consulta que comienza con & es inofensiva, ¿por qué no dejarla así? En cualquier caso, te sugiero que busques el signo final y utilices \b para unir el principio de foo sin tomar en cuenta un personaje anterior:

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

Es un poco tonto pero empecé a tratar de resolver esto con una expresión regular y quería finalmente hacerlo funcionar 🙂

 $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 de reemplazo está en mal estado porque aparentemente se confunde si los personajes capturados son '&' s

Además, no coincide con afoo y similares.

Gracias. Sí, usa barras diagonales inversas para escapar, y tienes razón, no necesito el / ‘s.

Esto parece funcionar, aunque no lo hace en una línea como se solicitó en la pregunta original.

  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; } 

Me basé en su implementación para obtener una implementación Java que parece funcionar:

  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,""); } 

Tuve problemas en algunos casos con su implementación porque a veces no eliminó un & , y lo hizo con varios pasos que parece más fácil de entender.

Tuve un problema con su versión, particularmente cuando un parámetro estaba en la cadena de consulta varias veces (como param1 = toto & param2 = xxx & param1 = YYY & param3 = ZZZ & param1 ....)

Puede usar la siguiente expresión regular:

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

Si quiere hacer una coincidencia exacta, puede reemplazar (?.*?) Con un parámetro url. p.ej:

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

para hacer coincidir cualquier variable como foo=xxxx en cualquier URL.

Para cualquier persona interesada en reemplazar los parámetros de solicitud GET:

La siguiente expresión regular funciona también para consultas de método GET más generales (¿comenzando por?) Donde la respuesta marcada falla si el parámetro que se va a eliminar es el primero (¿después?)

Esta expresión regular (sabor JS) se puede utilizar para eliminar el parámetro independientemente de la posición (primero, último o intermedio), dejando la consulta en un estado bien formateado.

Así que solo usa una regex replace con una cadena vacía.

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

Básicamente coincide con uno de los tres casos mencionados anteriormente (de ahí las 2 tuberías)