从查询字符串中删除一个参数的正则表达式

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 = 
          '&': '&',
          '<': '&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>

【问题讨论】:

其他边缘情况:oopsfoo=123foofoo=---都是唯一、第一个、最后一个和中间参数。 (所以这里总共 12 个) @Roger Pate:谢谢,没想到。还有foobar=123foobarfoobar=,以确保foo的检查不会命中他们 如果输入是foo=,预期的输出是什么? @Mark Byers:空字符串。几分钟后,我将提供一个更完整的示例输出,当我得到我的测试脚本时... 谢谢,java版本好像是:String regex = "&"+paramToRemove+"(\\=[^&]*)?(?=&|$)|^"+paramToRemove+" (\\=[^&]*)?(&|$)"; 【参考方案1】:

如果你只想在一个正则表达式中做到这一点,你可以这样做:

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

这是因为您需要在 foo=... 之前匹配一个与号,或者在 foo=... 之后匹配一个与号,或者两者都不匹配,但不能同时匹配两者。

说实话,我认为你这样做的方式更好:在单独的步骤中删除尾随 &。

【讨论】:

为什么两者都无效?输入:?blah&amp;foo=abc&amp;blah @Roger Pate:两者都是有效输入,但您只想匹配其中一个(因为我正在替换与空字符串匹配的任何内容) 尝试针对 Roger 的测试用例运行此模式。 接受这个是因为我为所有测试用例工作的解决方案(请参阅我的问题的编辑)是这个想法的修改版本:/&amp;foo(\=[^&amp;]*)?(?=&amp;|$)|^foo(\=[^&amp;]*)?(&amp;|$)/ 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,我会使用一个单独的解析器,它可能在其中使用正则表达式,但实际上仍能理解格式)——一种解决方案是确保每个参数只有一个与号:将前导的 ? 替换为 &amp;

这给出了/&amp;foo(=[^&amp;]*)?(?=&amp;|$)/,这是非常直接的,也是您将获得的最好的。删除最终结果中的前导&amp;(或将其改回?等)。修改测试用例使用与上述相同的用例,并将循环更改为:

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&amp;foobar=456&amp;foo=bar=456&amp;foo=123 是的,我知道,这就是我说我的方法失败的原因。 :) +1 用于提供测试代码。即使您的解决方案不太奏效,测试代码也很有用。【参考方案3】:

有一个以&amp; 开头的查询字符串是无害的——为什么不这样呢?在任何情况下,我建议您搜索尾随 & 并使用 \b 来匹配 foo 的开头,而无需使用前一个字符:

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

【讨论】:

在第三个示例中使用尾随 & 符号会出现问题。 请注意,在我给出的正则表达式中,尾随 & 号是可选的。 是的,我想过留下额外的 &,但对我来说它看起来有点草率。此正则表达式将在结果上留下一个尾随 &。即\bfoo\=[^&amp;]+&amp;? -> bar=456&amp;。为了让它与foofoo= 一起工作,而不是与xfoofoox 一起工作,我将其修改为:/\bfoo(\=[^&amp;]*)?(&amp;|$)/【参考方案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";

替换部分搞砸了,因为如果捕获的字符是'&amp;'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,"");
  

在某些情况下,我在您的实施中遇到了麻烦,因为有时它没有删除 &amp;,而是通过多个看起来更容易理解的步骤来完成的。

我的版本有问题,特别是当参数多次出现在查询字符串中时(例如 param1=toto&param2=xxx&param1=YYY&param3=ZZZ&param1....)

【讨论】:

【参考方案7】:

您可以使用以下正则表达式:

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

如果您想进行完全匹配,可以将(?&lt;name&gt;.*?) 替换为 url 参数。 例如:

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

匹配任何 URL 中的任何变量,如 foo=xxxx

【讨论】:

【参考方案8】:

对于任何有兴趣替换 GET 请求参数的人:

以下正则表达式也适用于更一般的 GET 方法查询(以 ? 开头),如果要删除的参数是第一个参数(在 ? 之后),则标记的答案将失败

此(JS 风格)正则表达式可用于删除参数,无论位置如何(第一个、最后一个或介于两者之间),使查询处于格式良好的状态。

所以只需使用正则表达式替换为空字符串。

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

基本上它匹配上面提到的三种情况之一(因此有 2 个管道)

【讨论】:

在原始问题中,正则表达式的输入只是查询字符串(即?之后的所有内容),而不是整个url,因此字符串中没有?。这就是为什么接受的答案不考虑这种情况。 没错。但是我看不出这不符合条件,甚至更多我看不到任何反对投票的理由(!??),因为这个答案解决了一个非常常见、更普遍的情况。我已经用备注更新了答案。 我不是投反对票的人。但是其他人可能有的原因是,在您的原始答案中,您回答的问题与所问的问题不同,并且说接受的答案是错误的,因为它没有回答那个问题。 另外,您的答案未能删除原始帖子中几个边缘情况轮廓中的参数:foofoo&amp;bar=456bar=456&amp;fooabc=789&amp;foo&amp;bar=456foo=、@ 987654332@、xfoo=&amp;bar=456abc=789&amp;xfoo=&amp;bar=456xfoo=123&amp;bar=456abc=789&amp;xfoo=123&amp;bar=456 这是一个 jsFiddle 显示答案(来自 OP):jsfiddle.net/1b6ukaw9 这是一个 jsFiddle,显示您的正则表达式失败的情况:jsfiddle.net/o0b2rrkd 此正则表达式适用于您在 perl/php 中的情况: /&amp;foo(\=[^&amp;]*)?(?=&amp;|$)|^foo(\=[^&amp;]*)?(&amp;|$)|(?&lt;=\?)foo(\=[^&amp;]*)?(&amp;|$)/。但它在 Javascript 中不起作用,因为它不支持后视断言。这是一个在 Javascript 中工作的版本,但我也必须更改替换代码:jsfiddle.net/ba7m8wz8

以上是关于从查询字符串中删除一个参数的正则表达式的主要内容,如果未能解决你的问题,请参考以下文章

使用正则表达式删除空参数[复制]

使用正则表达式从 JSON 中删除具有给定属性的对象

使用正则表达式从 C# 中的 SQL 语句中查找 SQL 函数

用于仅从字符串中删除特定特殊字符的正则表达式

从正则表达式匹配中删除最后一个字符[重复]

BigQuery 正则表达式从字符串中删除/替换文本列表