Подтвердить что ты не робот

Регулярное выражение для удаления одного параметра из строки запроса

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

Скажем, я хочу удалить параметр 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>
4b9b3361

Ответ 1

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

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

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

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

Ответ 2

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

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

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

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

Ответ 3

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

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

Ответ 4

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

$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 и тому подобное.

Ответ 5

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

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

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

Ответ 6

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

  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....)

Ответ 7

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

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

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

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

чтобы сопоставить любую переменную типа foo=xxxx в любом URL-адресе.

Ответ 8

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

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

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

Поэтому просто замените регулярное выражение на пустую строку.

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

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