Регулярное выражение для удаления одного параметра из строки запроса - PullRequest
32 голосов
/ 03 декабря 2009

Я ищу регулярное выражение для удаления одного параметра из строки запроса, и я хочу сделать это в одном регулярном выражении, если это возможно.

Скажите, что я хочу удалить параметр foo. Прямо сейчас я использую это:

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

Это работает до тех пор, пока foo не является первым параметром в строке запроса. Если это так, то моя новая строка запроса начинается с амперсанда. (Например, «foo=123&bar=456» дает результат «&bar=456».) Сейчас я просто проверяю после регулярного выражения, начинается ли строка запроса с амперсанда, и отключаю его, если это так.

Пример крайних случаев:

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

Редактировать

Хорошо, как отмечалось в комментариях, есть гораздо больше крайних случаев, чем я первоначально рассматривал. Я получил следующее регулярное выражение для работы со всеми из них:

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

Это изменение от Ответ Марка Байерса , поэтому я принимаю его, но вклад Роджера Пейта тоже очень помог.

Вот полный набор тестовых примеров, которые я использую, и фрагмент Javascript, который их тестирует:

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

Ответы [ 8 ]

21 голосов
/ 03 декабря 2009

Если вы хотите сделать это только одним регулярным выражением, вы можете сделать это:

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

Это потому, что вам нужно сопоставить либо амперсанд перед foo = ..., либо один после, либо ни один, но не оба.

Если честно, я думаю, что лучше, чем вы это сделали: удаление заднего амперсанда в отдельном шаге.

6 голосов
/ 03 декабря 2009
/(?<=&|\?)foo(=[^&]*)?(&|$)/

Использует lookbehind и последнюю группу для "привязки" совпадения и допускает пропущенное значение. Измените \? на ^, если вы уже удалили знак вопроса из строки запроса.

Однако Regex все еще не заменяет реальный анализатор строки запроса.

Обновление: Тестовый скрипт: (запустите его на 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"

Это показывает, где мой подход не удался, Марк имеет право на это - это должно показать, почему вы не должны делать это с регулярным выражением ..: P


Проблема в том, что параметр запроса ассоциируется только с одним амперсандом, и - если вы должны использовать регулярное выражение (если вы его не взяли: P, я бы использовал отдельный анализатор, который мог бы использовать внутри него регулярное выражение, но все же на самом деле понимают формат) - одним из решений было бы убедиться, что для каждого параметра есть только один амперсанд: замените начальный ? на &.

Это дает /&foo(=[^&]*)?(?=&|$)/, что очень прямолинейно и лучшее, что вы собираетесь получить. Удалите ведущий & в конечном результате (или измените его на ? и т. Д.). Чтобы изменить тестовый пример, используйте те же случаи, что и выше, и измените цикл на:

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"
4 голосов
/ 03 декабря 2009

Наличие строки запроса, начинающейся с &, безвредно - почему бы не оставить ее такой? В любом случае я предлагаю вам найти конечный амперсанд и использовать \b, чтобы соответствовать началу foo без взятия предыдущего символа:

 /\bfoo\=[^&]+&?/
1 голос
/ 27 ноября 2012

Я основывался на вашей реализации, чтобы получить Java-реализацию, которая, кажется, работает:

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

В некоторых случаях у меня были проблемы с вашей реализацией, потому что иногда она не удаляла &, а делала это в несколько шагов, что кажется более понятным.

У меня была проблема с вашей версией, особенно когда параметр был в строке запроса несколько раз (например, param1 = toto & param2 = xxx & param1 = YYY & param3 = ZZZ & param1 ....)

1 голос
/ 13 апреля 2010

Спасибо. Да, он использует обратную косую черту для выхода, и вы правы, мне не нужны /'s.

Кажется, что это работает, хотя и не делает это в одной строке, как было запрошено в исходном вопросе.

    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 голос
/ 04 декабря 2009

Это немного глупо, но я начал пытаться решить эту проблему с помощью регулярного выражения и хотел, наконец, заставить его работать:)

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

часть замены испорчена, потому что, очевидно, она запутывается, если захваченные символы '&' s

Кроме того, оно не соответствует afoo и т. П.

0 голосов
/ 20 октября 2015

Для всех, кто заинтересован в замене параметров запроса GET:

Следующее регулярное выражение работает и для более общих запросов метода GET (начиная с?), Где помеченный ответ заканчивается неудачей, если удаляемый параметр является первым (после?)

Это (JS flavour) регулярное выражение может использоваться для удаления параметра независимо от позиции (первая, последняя или между), оставляя запрос в хорошо отформатированном состоянии.

Так что просто используйте регулярное выражение заменить на пустую строку.

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

В основном это соответствует одному из трех случаев, упомянутых выше (отсюда 2 канала)

0 голосов
/ 05 июня 2013

Вы можете использовать следующее регулярное выражение:

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

Если вы хотите сделать точное соответствие, вы можете заменить (?<name>.*?) параметром url. e.g.:

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

соответствует любой переменной, например foo=xxxx в любом URL.

...