从查询字符串中删除一个参数的正则表达式
Posted
技术标签:
【中文标题】从查询字符串中删除一个参数的正则表达式【英文标题】:Regular expression to remove one parameter from query string 【发布时间】:2010-12-22 23:54:41 【问题描述】:我正在寻找一个正则表达式来从查询字符串中删除单个参数,如果可能的话,我想在单个正则表达式中执行此操作。
假设我想删除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
编辑
好的,正如 cmets 中指出的那样,边缘情况比我最初考虑的要多得多。我得到了以下正则表达式来处理所有这些:
/&foo(\=[^&]*)?(?=&|$)|^foo(\=[^&]*)?(&|$)/
这是从 Mark Byers's answer 修改的,这就是我接受它的原因,但 Roger Pate 的意见也有很大帮助。
这是我正在使用的全套测试用例,以及用于测试它们的 javascript sn-p:
$(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>
【问题讨论】:
其他边缘情况:oopsfoo=123
、foo
、foo=
---都是唯一、第一个、最后一个和中间参数。 (所以这里总共 12 个)
@Roger Pate:谢谢,没想到。还有foobar=123
、foobar
和foobar=
,以确保foo
的检查不会命中他们
如果输入是foo=
,预期的输出是什么?
@Mark Byers:空字符串。几分钟后,我将提供一个更完整的示例输出,当我得到我的测试脚本时...
谢谢,java版本好像是:String regex = "&"+paramToRemove+"(\\=[^&]*)?(?=&|$)|^"+paramToRemove+" (\\=[^&]*)?(&|$)";
【参考方案1】:
如果你只想在一个正则表达式中做到这一点,你可以这样做:
/&foo(=[^&]*)?|^foo(=[^&]*)?&?/
这是因为您需要在 foo=... 之前匹配一个与号,或者在 foo=... 之后匹配一个与号,或者两者都不匹配,但不能同时匹配两者。
说实话,我认为你这样做的方式更好:在单独的步骤中删除尾随 &。
【讨论】:
为什么两者都无效?输入:?blah&foo=abc&blah
@Roger Pate:两者都是有效输入,但您只想匹配其中一个(因为我正在替换与空字符串匹配的任何内容)
尝试针对 Roger 的测试用例运行此模式。
接受这个是因为我为所有测试用例工作的解决方案(请参阅我的问题的编辑)是这个想法的修改版本:/&foo(\=[^&]*)?(?=&|$)|^foo(\=[^&]*)?(&|$)/
gbacon:唯一失败的情况是那些包含 'foo' 而没有值的情况。我已经更新了正则表达式来处理这个问题,它现在通过了所有案例。【参考方案2】:
/(?<=&|\?)foo(=[^&]*)?(&|$)/
使用lookbehind 和最后一组来“锚定”匹配,并允许缺少值。如果您已经从查询字符串中去掉了问号,请将 \?
更改为 ^
。
然而,正则表达式仍然不能替代查询字符串的真正解析器。
更新: 测试脚本:(在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"
【讨论】:
这个有一些问题,但我正在努力。是的,没有\?,我的字符串只是查询字符串 以下输入失败:bar=456&foo
、bar=456&foo=
、bar=456&foo=123
是的,我知道,这就是我说我的方法失败的原因。 :)
+1 用于提供测试代码。即使您的解决方案不太奏效,测试代码也很有用。【参考方案3】:
有一个以&
开头的查询字符串是无害的——为什么不这样呢?在任何情况下,我建议您搜索尾随 & 并使用 \b
来匹配 foo 的开头,而无需使用前一个字符:
/\bfoo\=[^&]+&?/
【讨论】:
在第三个示例中使用尾随 & 符号会出现问题。 请注意,在我给出的正则表达式中,尾随 & 号是可选的。 是的,我想过留下额外的 &,但对我来说它看起来有点草率。此正则表达式将在结果上留下一个尾随 &。即\bfoo\=[^&]+&?
-> bar=456&
。为了让它与foo
或foo=
一起工作,而不是与xfoo
或foox
一起工作,我将其修改为:/\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¶m2=xxx¶m1=YYY¶m3=ZZZ¶m1....)
【讨论】:
【参考方案7】:您可以使用以下正则表达式:
[\?|&](?<name>.*?)=[^&]*&?
如果您想进行完全匹配,可以将(?<name>.*?)
替换为 url 参数。
例如:
[\?|&]foo=[^&]*&?
匹配任何 URL 中的任何变量,如 foo=xxxx
。
【讨论】:
【参考方案8】:对于任何有兴趣替换 GET 请求参数的人:
以下正则表达式也适用于更一般的 GET 方法查询(以 ? 开头),如果要删除的参数是第一个参数(在 ? 之后),则标记的答案将失败
此(JS 风格)正则表达式可用于删除参数,无论位置如何(第一个、最后一个或介于两者之间),使查询处于格式良好的状态。
所以只需使用正则表达式替换为空字符串。
/&s=[^&]*()|\?s=[^&]*$|s=[^&]*&/
基本上它匹配上面提到的三种情况之一(因此有 2 个管道)
【讨论】:
在原始问题中,正则表达式的输入只是查询字符串(即?
之后的所有内容),而不是整个url,因此字符串中没有?
。这就是为什么接受的答案不考虑这种情况。
没错。但是我看不出这不符合条件,甚至更多我看不到任何反对投票的理由(!??),因为这个答案解决了一个非常常见、更普遍的情况。我已经用备注更新了答案。
我不是投反对票的人。但是其他人可能有的原因是,在您的原始答案中,您回答的问题与所问的问题不同,并且说接受的答案是错误的,因为它没有回答那个问题。
另外,您的答案未能删除原始帖子中几个边缘情况轮廓中的参数:foo
、foo&bar=456
、bar=456&foo
、abc=789&foo&bar=456
、foo=
、@ 987654332@、xfoo=&bar=456
、abc=789&xfoo=&bar=456
、xfoo=123&bar=456
、abc=789&xfoo=123&bar=456
这是一个 jsFiddle 显示答案(来自 OP):jsfiddle.net/1b6ukaw9 这是一个 jsFiddle,显示您的正则表达式失败的情况:jsfiddle.net/o0b2rrkd 此正则表达式适用于您在 perl/php 中的情况: /&foo(\=[^&]*)?(?=&|$)|^foo(\=[^&]*)?(&|$)|(?<=\?)foo(\=[^&]*)?(&|$)/
。但它在 Javascript 中不起作用,因为它不支持后视断言。这是一个在 Javascript 中工作的版本,但我也必须更改替换代码:jsfiddle.net/ba7m8wz8以上是关于从查询字符串中删除一个参数的正则表达式的主要内容,如果未能解决你的问题,请参考以下文章