web-dev-qa-db-fra.com

Expression régulière pour supprimer un paramètre de la chaîne de requête

Je recherche une expression régulière pour supprimer un seul paramètre d'une chaîne de requête et je souhaite le faire dans une seule expression régulière si possible.

Dites que je veux supprimer le paramètre foo. En ce moment j'utilise ceci:

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

Cela fonctionne tant que foo n'est pas le premier paramètre de la chaîne de requête. Si c'est le cas, ma nouvelle chaîne de requête commence par une esperluette. (Par exemple, "foo=123&bar=456" donne un résultat de "&bar=456".) Pour le moment, je vérifie simplement après la regex si la chaîne de requête commence par esperluette et la coupe si elle le fait.

Exemple de cas Edge:

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

Modifier

OK, comme indiqué dans les commentaires, il y a beaucoup plus de cas Edge que ce que j'avais initialement envisagé. J'ai la regex suivante pour travailler avec chacun d'eux:

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

Ceci est modifié à partir de Réponse de Mark Byers , c'est pourquoi j'accepte cela, mais la contribution de Roger Pate a également beaucoup aidé.

Voici la suite complète des cas de test que j'utilise et un extrait de code Javascript qui les teste:

$(function() {
    var regex = /&foo(\=[^&]*)?(?=&|$)|^foo(\=[^&]*)?(&|$)/;
    
    var escapeHtml = function (str) {
        var map = {
          '&': '&',
          '<': '&lt;',
          '>': '&gt;',
          '"': '&quot;',
          "'": '&#039;'
        };
        
        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(
            '<tr class="' + (success ? 'passed' : 'failed') + '">'
            + '<td>' + (success ? 'PASS' : 'FAIL') + '</td>'
            + '<td>' + escapeHtml(tests[i]) + '</td>'
            + '<td>' + escapeHtml(output) + '</td>'
            + '<td>' + escapeHtml(expected[i]) + '</td>'
            + '</tr>'
        );
    }
    
});
#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;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table id="output">
    <tr>
        <th>Succ?</th>
        <th>Input</th>
        <th>Output</th>
        <th>Expected</th>
    </tr>
</table>

29
Kip

Si vous voulez faire cela dans une seule expression régulière, vous pouvez le faire: 

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

En effet, vous devez faire correspondre une esperluette avant le mot-clé = ..., une réponse après, ou aucune des deux, mais pas les deux.

Pour être honnête, je pense que c'est mieux comme vous l'avez fait: supprimer l'esperluette en queue dans une étape séparée.

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

Utilise lookbehind et le dernier groupe pour "ancrer" la correspondance et autorise une valeur manquante. Changez le \? en ^ si vous avez déjà enlevé le point d'interrogation de la chaîne de requête.

Regex n'est toujours pas un substitut pour un analyseur réel de la chaîne de requête.

Mise à jour: Script de test: (exécutez-le à 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"

Cela montre où mon approche a échoué, Mark en a le droit - ce qui devrait montrer pourquoi vous ne devriez pas le faire avec regex ..: P


Le problème consiste à associer le paramètre de requête à exactement une esperluette et, si vous devez utiliser regex (si vous ne l’avez pas déjà lu: P, j’utiliserais un analyseur distinct, qui pourrait utiliser regex à l’intérieur, comprendre le format) - une solution serait de s’assurer qu’il existe exactement une esperluette par paramètre: remplacez le ? initial par un &.

Cela donne /&foo(=[^&]*)?(?=&|$)/, qui est très simple et le meilleur que vous puissiez obtenir. Supprimez le & qui précède dans le résultat final (ou remplacez-le par un ?, etc.). Modifier le scénario de test à cette fin utilise les mêmes scénarios que ci-dessus et modifie la boucle en:

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"
5
Roger Pate

Avoir une chaîne de requête qui commence par & est inoffensif - pourquoi ne pas le laisser ainsi? Dans tous les cas, je vous suggère de rechercher l'esperluette de fin et d'utiliser \b pour faire correspondre le début de foo sans prendre un caractère précédent:

 /\bfoo\=[^&]+&?/
4
JSBձոգչ

Merci. Oui, il utilise des barres obliques inverses pour s’échapper, et vous avez raison, je n’ai pas besoin des /.

Cela semble fonctionner, même s'il ne le fait pas en une seule ligne, comme le demandait la question initiale.

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

Je me suis basé sur votre implémentation pour obtenir un implément Java qui semble fonctionner:

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

Votre mise en œuvre m'a parfois posé problème car il ne supprimait parfois pas un & et le faisait en plusieurs étapes, ce qui semble plus facile à comprendre.

J'ai eu un problème avec votre version, en particulier lorsqu'un paramètre était dans la chaîne de requête plusieurs fois (comme param1 = toto & param2 = xxx & param1 = YYY & param3 = ZZZ & param1 ...)

1
Sebastien Lorber

C'est un peu bête, mais j'ai commencé à essayer de résoudre ce problème avec une expression rationnelle et je voulais enfin le faire fonctionner :)

$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 partie de remplacement est gâchée parce qu'apparemment, cela devient confus si les caractères capturés sont '&'s

De plus, il ne correspond pas ne correspond pas à afoo, etc.

1
Matteo Riva

Vous pouvez utiliser l'expression régulière suivante:

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

Si vous voulez faire une correspondance exacte, vous pouvez remplacer (?<name>.*?) par un paramètre url. Par exemple:

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

pour faire correspondre une variable comme foo=xxxx dans n'importe quelle URL.

0
Sujit Rai