Регулярное выражение для удаления одного параметра из строки запроса
Я ищу регулярное выражение для удаления одного параметра из строки запроса, и я хочу сделать это в одном регулярном выражении, если это возможно.
Скажем, я хочу удалить параметр 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 = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": '''
};
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>
Ответы
Ответ 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 трубы)